Reputation: 2304
I am not using ARC.
Testing for leaks through Instruments gives me the following when presenting a UIAlertController:
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
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