I have an App that compiles a list of customer specific reports for a customer after login. These reports are showing up and I have put a "view" button which is supposed to download the PDF file, and bring it up to view within the app.

At this stage, it looks as though I have a memory issue when the view button is pressed, and I am not sure how to find where the issue is. HEre is my code:

#import "reportsTestViewController.h"
#import "ReportsDataObject.h"
#import "Session.h"
#import "AFNetworking.h"
@interface reportsTestViewController ()

@end

@implementation reportsTestViewController

@synthesize response;

@synthesize myDataIvar;

@synthesize viewReportPressed;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    reportsTable.delegate = self;
    reportsTable.dataSource = self;
    self.sections = [[NSMutableDictionary alloc] init];

    [super viewDidLoad];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

#pragma mark NSURLConnection Delegate Methods
//    

    //Create your request pointing to the test page
   NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.tesg.com.au/allCustBuild.php"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];


    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];

    //initialize it when you create your connection
    if (connection){
        self.myDataIvar = [[NSMutableData alloc] init];
    }
}

    -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
        [self.myDataIvar setLength:0];
    }

    -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
        [self.myDataIvar appendData:data];

    }

    -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
        NSLog(@"Connection Failed: %@", error.userInfo);
    }

-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
    //this is where you would parse the data received back from the server
    NSString *responseString = [[NSString alloc] initWithData:self.myDataIvar encoding:NSUTF8StringEncoding];
    NSLog(@"Received Data: %@",responseString);
    [self setupReportsFromJSONArray:self.myDataIvar];

}

-(void)connectionWasASuccess:(NSData *)data{
    [self setupReportsFromJSONArray:data];

}



-(void)setupReportsFromJSONArray:(NSData*)dataFromReportsArray{
    BOOL found;
    NSError *error;
   // NSMutableArray *reportsArray = [[NSMutableArray alloc] init];
    NSArray *arrayFromServer = [NSJSONSerialization JSONObjectWithData:dataFromReportsArray options:0 error:&error];

    if(error){
        NSLog(@"error parsing the json data from server with error description - %@", [error localizedDescription]);
    }

    else {
        reportsArray = [[NSMutableArray alloc] init];
        for(NSDictionary *eachReport in arrayFromServer)
        {

            ReportsDataObject *report = [[ReportsDataObject alloc] initWithJSONData:eachReport];

            [reportsArray addObject:report];
            NSString *c = [[eachReport objectForKey:@"title"] substringToIndex:3];
                      found = NO;

            for (NSString *str in [self.sections allKeys])
            {
                if ([str isEqualToString:c])
                {
                    found = YES;
                }
            }

            if (!found)
            {
                [self.sections setValue:[[NSMutableArray alloc] init] forKey:c];
            }
        }

        }
        NSLog(@"Array Populated");
        NSLog(@"%u reports found",reportsArray.count);
        //Now you have your reportsArray filled up with all your data objects



for (NSDictionary *eachReport in arrayFromServer)
{
   [[self.sections objectForKey:[[eachReport objectForKey:@"title"] substringToIndex:3]] addObject:eachReport];
}

// Sort each section array
for (NSString *key in [self.sections allKeys])
{
    [[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES]]];


}

[reportsTable reloadData];
}


-(void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)viewReportPressed:(UIButton*)button {

    NSLog(@"Button successfully Pressed");
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

    NSURL *URL = [NSURL URLWithString:@"http://tesg.com.au/portal/media/reports/1367365180_367 Collins Passive April 2013.pdf"];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];

    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *theResponse) {
        NSURL *documentsDirectoryPath = [NSURL fileURLWithPath:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]];
        return [documentsDirectoryPath URLByAppendingPathComponent:[theResponse suggestedFilename]];
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        NSLog(@"File downloaded to: %@", filePath);
    }];
    [downloadTask resume];

                                              }


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{

    NSUInteger count = [[self.sections allKeys] count];
    NSLog(@"Number of sections: %d", count);
    return count;
}




- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return [[[self.sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] objectAtIndex:section];
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    //We check against table to make sure we are displaying the right number of cells
    // for the appropriate table. This is so that things will work even if one day you
    //decide that you want to have two tables instead of one.

    {
        NSUInteger count = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] objectAtIndex:section]] count];
        NSLog(@"Number of rows in section: %d", count);
        return count;
    }
}
//- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
        //return [[self.sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
   // }





-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{


    static NSString *cellIdentifier = @"Cell";



    UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (!cell)
    {


        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];

    }

        //The beauty of this is that you have all your data in one object and grab WHATEVER you like
    //This way in the future you can add another field without doing much.
    NSUInteger count = [[self.sections allKeys] count];
    if(count == 0){
        cell.textLabel.text = @"no reports to show";
    }
    else{
        NSDictionary *Reports = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];

        cell.textLabel.text = [Reports objectForKey:@"title"];
        cell.detailTextLabel.text = [Reports objectForKey:@"building"];








        UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [button addTarget:self action:@selector(viewReportPressed:) forControlEvents:UIControlEventTouchUpInside];
        [button setTitle:@"View" forState:UIControlStateNormal];

        button.frame = CGRectMake(180.0f, 5.0f, 150.0f, 30.0f);
        [cell addSubview:button];


        // in the future you can grab whatever data you need like this
        //[currentReport buildingName], or [currentReport reportName];
    }


    return(cell);

        }




@end

When the view button is pressed in any of the cells, I get the NSLog saying 'button successfully pressed' but it crashes immediately after with error

[downloadTask resume];             Thread 1:EXC_BAD_ACCESS (code=2, address=0x0)

Any ideas how to fix this?

有帮助吗?

解决方案

If you get a EXC_BAD_ACCESS with an address of 0x0, that generally means that a nil value was used in a context where nil is not permitted.

In this case, it is a result of the fact that your URL string is not valid. Spaces are not allowed. Thus your resulting URL variable is nil, which will result in the behavior you describe. You probably want to percent escape the URL string, e.g.:

NSString *URLString = @"http://tesg.com.au/portal/media/reports/1367365180_367 Collins Passive April 2013.pdf";
NSURL *URL = [NSURL URLWithString:[URLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

To identify this sort of exception in the future, you can use exception breakpoints, which will often help identify the offending line of code. Or just carefully review your code code for situations where you may have unintentionally passed a nil value to a method that does not accept it.


If, however, you ever get a EXC_BAD_ACCESS with a value other than 0x0, that is often a result of an attempt to use a previously deallocated object. (Not always, but frequently. Fortunately, ARC has made this sort of problem less common.) Clearly, this is not the case here, but you asked how one would identify a situation where you attempt to use a previously deallocated object.

To diagnose that situation, you would generally turn on "zombies" (see Running Your Application with Diagnostics). By the way, once you finish your diagnostics with zombies, make sure you turn them back off, as you don't want to keep zombies turned on (especially in a production app).

Anyway, in this case, because the address was 0x0, it wasn't a result of a deallocated object, but rather the problem was the incorrect usage of a nil value, caused by the invalid URL.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top