Ash
Ash

Reputation: 2304

UIAlertController memory leak

I am not using ARC.

Testing for leaks through Instruments gives me the following when presenting a UIAlertController:

enter image description here

It seems to be complaining about this block of code when I examine the call tree. Not sure how much of this is relevant but anyway...

-(void) connection:(NSURLConnection*)connection didFailWithError:(NSError*)error {
  // bunch of code

  #ifndef NDEBUG
    NSString* err_msg = [NSString stringWithFormat:@"%@%ld", @"Error number ", (long) error_code];
    // @property (nonatomic, retain) id <DownloadMonitor> m_downloadMonitor;
    [_m_downloadMonitor showDownloadAlert:err_msg withTitle:@"Download error"];
  #endif

m_downloadMonitor is actually an object of type DashboardController, defined as follows:

@interface DashboardController : BaseController <UIAlertViewDelegate, UITableViewDelegate, UITableViewDataSource, DownloadMonitor>

DownloadMonitor is a custom protocol defined as follows:

@protocol DownloadMonitor <NSObject>
-(void) downloadFinishedFor:(UIProgressView*)progress_bar;
-(void) downloadFailedFor:(UIProgressView*)progress_bar;
-(void) showDownloadAlert:(NSString*)message withTitle:(NSString*)title;
@end

The method showDownloadAlert is defined in DashboardController as follows:

-(void) showDownloadAlert:(NSString*)message withTitle:(NSString*)title {
  [self showPopupMessage:message withTitle:title andDelegate:self andActionHandlers:@{@"OK":@""}];
}

And finally, showPopupMessage in BaseController (the parent class of DashboardController):

- (void)showPopupMessage: (NSString*)message withTitle:(NSString*)title andDelegate:(BaseController*)delegate andActionHandlers:(NSDictionary*)handlers {
  UIAlertController* alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];

  for ( id key in handlers ) {
    NSString* button_name = (NSString*) key;
    NSString* handler_name = (NSString*) [handlers objectForKey:key];
    UIAlertAction* action;

    if ( ! [handler_name isEqualToString:@""] ) {
      SEL sel = NSSelectorFromString(handler_name);
      action = [UIAlertAction actionWithTitle:button_name style:UIAlertActionStyleDefault handler:^(UIAlertAction * action)         {
          [delegate performSelector:sel];
          [alert dismissViewControllerAnimated:YES completion:NULL];
        }];
    }
    else {
      action = [UIAlertAction actionWithTitle:button_name style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
      [alert dismissViewControllerAnimated:YES completion:NULL];
        }];
    }

    [alert addAction:action];
  }

  [delegate presentViewController:alert animated:YES completion:nil];
}

Why is Instruments displaying a leak? I had a look at these threads:

iOS 8 Only Memory Leak with UIAlertController or UIActionSheet

UIAlertController memory leak/issues - Swift

They seem to suggest it might be a bug...or it could be a retain cycle that I've missed.

Upvotes: 3

Views: 1781

Answers (1)

Nicolas Buquet
Nicolas Buquet

Reputation: 3955

I think you have a retain circle:

In showPopupMessage method, actions are added to alert which retains them. You define blocks that references alert (because it uses it). The blocks are retaining alert.

So, alert retains blocks which retain `'alert: your retain circle!

You should try:

__block __typeof__(alert) blockAlert = alert;

action = [UIAlertAction actionWithTitle:button_name style:UIAlertActionStyleDefault handler:^(UIAlertAction * action)         {
    [delegate performSelector:sel];
    [blockAlert dismissViewControllerAnimated:YES completion:NULL];
}];

The __block storage type modifier will reference alert without retaining it (in non-ARC mode): https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW6

Upvotes: 2

Related Questions