UIAlertController 内存泄漏问题

3
我没有使用ARC。
通过Instruments测试泄漏时,当呈现UIAlertController时,会出现以下情况:

enter image description here

当我检查调用树时,它似乎在抱怨这个代码块。不确定其中多少是相关的,但无论如何,请看...
-(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 实际上是一个类型为 DashboardController 的对象,定义如下:

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

DownloadMonitor 是一个自定义协议,定义如下:

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

方法showDownloadAlertDashboardController中定义如下:
-(void) showDownloadAlert:(NSString*)message withTitle:(NSString*)title {
  [self showPopupMessage:message withTitle:title andDelegate:self andActionHandlers:@{@"OK":@""}];
}

最后,DashboardController 的父类 BaseController 中的 showPopupMessage 方法:
- (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];
}

为什么Instruments显示泄漏?

我看了这些线程:

iOS 8 Only Memory Leak with UIAlertController or UIActionSheet

UIAlertController memory leak/issues - Swift

它们似乎表明可能是一个错误......或者是我错过的保留周期。

1个回答

2
我认为你有一个保留环:
showPopupMessage方法中,将操作添加到alert中,从而保留了它们。您定义了引用alert的块(因为它使用它)。这些块会保留alert
因此,alert保留了保留块,从而保留了“alert:您的保留环!”。
你应该尝试:
__block __typeof__(alert) blockAlert = alert;

action = [UIAlertAction actionWithTitle:button_name style:UIAlertActionStyleDefault handler:^(UIAlertAction * action)         {
    [delegate performSelector:sel];
    [blockAlert dismissViewControllerAnimated:YES completion:NULL];
}];
__block 存储类型修饰符将不会在非 ARC 模式下保留对 alert 的引用: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW6。请注意,本文链接为英文原文。

谢谢!这似乎已经阻止了它报告相同的泄漏,但现在它抱怨这一行:UIAlertController* alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; - Ash
另外,我猜你的代码应该替换这一行中的 alert[alert dismissViewControllerAnimated:YES completion:NULL]; 是吗? - Ash
是的,你说得对:我把delegate(我的第一个怀疑变量)和alert弄混了。delegate必须保持不变,但alert必须用弱引用的__block修饰符替换为blockAlert。如果我的答案正确,请别忘了将其标记为正确/接受的答案。 - Nicolas Buquet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接