3

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.

Community
  • 1
  • 1
Ash
  • 2,021
  • 2
  • 26
  • 59

1 Answers1

2

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

Nicolas Buquet
  • 3,880
  • 28
  • 28
  • Thank you! That seems to have stopped it from reporting the same leak, but now it compains about this line: `UIAlertController* alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];` – Ash Oct 14 '15 at 06:10
  • Also, I'm guessing your code was supposed to replace `alert` in this line `[alert dismissViewControllerAnimated:YES completion:NULL];` ? – Ash Oct 14 '15 at 06:11
  • Yes, you're right: I switched `delegate` (which was my first suspect variable) with `alert`. `delegate`must stay as is, but àlert`must be replaced with weak `__block`ref `blockAlert`. And if my answer was correct, don't forget to mark it as the correct/accepted answer. – Nicolas Buquet Oct 14 '15 at 08:47