简体   繁体   中英

How to fix the memory leaks in Objective-C?

I built a simple app which gets the reports from HockeyApp. However when I run the app with the memory leak instruments, it showed there was a memory leak when I perform the getReport action. I couldn't understand all the information shown in the instrument.

Here is the button action method which causes the memory leak:

- (IBAction)getReports:(id)sender {

//initialize url that is going to be fetched.
NSURL *url = [NSURL URLWithString:@"https://rink.hockeyapp.net/api/2/apps/APP_ID/crash_reasons"];

//initialize a request from url
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:tokenReceived forHTTPHeaderField:@"X-HockeyAppToken"];

[request setHTTPMethod:@"GET"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

//initialize a connection from request
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
self.getReportConnection = connection;

}


- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data{

    if (connection==getReportConnection) {

     [self.receivedData appendData:data];

     NSLog(@"data is %@",data);

    NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    NSError *e = nil;
    NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding];

    NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:jsonData options: NSJSONReadingMutableContainers error: &e];
    NSLog(@"login json is %@",JSON);
     NSLog(@"reason json is %@",JSON[@"reason"]);

    [JSON[@"crash_reasons"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {


        [reportArray addObject:obj[@"reason"]];
         NSLog(@"index = %lu, Object For title Key = %@", (unsigned long)idx, obj[@"reason"]);
    }];

    NSError *error = nil;
    NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:jsonData
                                                         options:kNilOptions error:&error];

    if (error != nil) {
        NSLog(@"Error parsing JSON.");
    }
    else {
        NSLog(@"Array: %@,array count is %d", jsonArray,jsonArray.count);
    }

   // [reportArray addObject:[jsonArray objectAtIndex:0]];

    if (JSON!=NULL) {
        UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Reports succesfully retrieved" message:@"" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles: nil];
        [alert show];
    }

       }
}

 // This method receives the error report in case of connection is not made to server.
 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{

UIAlertView *errorAlert=[[UIAlertView alloc]initWithTitle:@"Wrong Login" message:nil delegate:self cancelButtonTitle:@"ok" otherButtonTitles: nil];
[errorAlert show];
NSLog(@"error is %@",error);
}

// This method is used to process the data after connection has made successfully.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

}

I see that the memory leak occurs just before the alert view appears in the didRecieveData method.

Here is the screenshot of the memory leak instrument showing memory leak:

在此处输入图片说明

I couldn't understand which part of the code causes the memory leak. Can anyone tell me how to identify the part of code that causes the memory leak with the leak instrument?

Edit: When I run the app on the simulator, the instrument didnt show any memory leak:

Here is the screenshot:在此处输入图片说明

Again When I run the app on device, the instrument showed me the memory leak:在此处输入图片说明

I looked into the leaks section and I found NSmutableArray is causing the leak: 在此处输入图片说明在此处输入图片说明

I used only one NSMutableArray in my code. I declared it in .h file:

@property (nonatomic,strong) NSMutableArray *reportArray;

and allocated it in viewDidLoad :

reportArray=[[NSMutableArray alloc]init];

and loaded it in didRecieveData :

 [reportArray addObject:obj[@"reason"]];

Stacktrace snapshots:

在此处输入图片说明在此处输入图片说明在此处输入图片说明

Try this:

reportArray = [[[NSMutableArray alloc] init] autorelease];

in your connectionDidFinishLoading: and connection:didFailWithError: methods set

reportArray = nil

and finally in Project > Build Phases > Compile Sources add -fno-objc-arc as compiler flag for this file (edited, sorry) . Then click Product menu >Analyze (command + shift + B) again and check if the memory leak still occurs.

It is possible it is Apple's leak -- sure looks like it is coming from UIAlertView / UIAlertConnection. You might try implementing the alert using UIAlertConnection and see if it goes away -- Apple may not have tested the back-compatible UIAlertView implementation as much.

It won't show up in leaks, but be aware that NSURLConnection retains its delegate, and in your case you have your delegate retaining the NSURLConnection it looks like. That should be a retain loop if I'm not mistaken. Be sure to break it (nil out the delegate, or nil out the connection on your controller) when the connection finishes or fails.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM