【问题标题】:ARC: Getting EXC_BAD_ACCESS from inside block used in delegate methodARC:从委托方法中使用的内部块获取 EXC_BAD_ACCESS
【发布时间】:2011-09-13 15:41:06
【问题描述】:

我一定是做错了什么,但是 Automatic Reference Counting 文档并没有给我提示它可能是什么。我正在做的是从委托方法内部调用一个带有块回调的方法。 从块内部访问同一个委托会导致访问错误。问题是我传递的对象 - loginController 将消息发送给它的委托 - 显然没有释放,当我不在块内访问它时,我可以多次调用该方法而无需一个问题。这是我的代码:

- (void)loginViewDidSubmit:(MyLoginViewController *)loginController
{
    NSString *user = loginController.usernameLabel.text;
    NSString *pass = loginController.passwordLabel.text;

    __block MyLoginViewController *theController = loginController;
    [self loginUser:user withPassword:pass callback:^(NSString *errorMessage) {
        DLog(@"error: %@", errorMessage);
        DLog(@"View Controller: %@", theController);    // omit this: all good
        theController = nil;
    }];
}

NSZombieEnabled 不记录任何内容,也没有来自 gdb 的可用堆栈跟踪。我在这里做错了什么?感谢您的任何指点!


编辑:

我认为问题的范围更大——上面的回调是从 NSURLConnectionDelegate 方法调用的(块本身是该委托的强属性,因此 ARC 应该调用 Block_copy())。在这种情况下我需要进行特殊测量吗?

Flow(loginController 始终可见):

登录控制器

[delegate loginViewDidSubmit:self];

查看委托

(method shown above calls the loginUser: method, which does something like:)
httpDelegate.currentCallback = callback;
httpDelegate.currentConnection = // linebreak for readability
    [[NSURLConnection alloc] initWithRequest:req
                                    delegate:httpDelegate
                            startImmediately:YES];

NSURLConnectionDelegate

- (void)connection:(NSURLConnection *)aConnection
  didFailWithError:(NSError *)error
{
    if (NULL != currentCallback) {
        currentCallback([error localizedDescription]);
        self.currentCallback = NULL;
    }
}

这是我获得错误访问权限的地方,但只有当我访问该 loginController 变量时...

【问题讨论】:

    标签: cocoa-touch objective-c-blocks automatic-ref-counting


    【解决方案1】:

    为属性设置复制属性,或者只为块调用“复制”方法。

    - (void)loginUser:(NSString *)user withPassword:(NSString *)pass callback:(void (^callback)(NSString *))
    {
        callback = [callback copy];
    

    【讨论】:

    • 这确实是正确的方法,我已将 strong 设置为属性属性,但在转换项目。
    【解决方案2】:

    实际的解决方案是我将块作为 strong 属性,但它应该是 copy 属性!哦!


    第一个“解决方案”:

    我刚刚找到了一种防止错误访问的方法。如上面我的编辑中所示,View Delegate 将块转发到 httpDelegate(另一个类的实例),而后者又保持对块的强引用。无论出于何种原因,将块分配给临时变量并转发临时块变量都可以解决问题。所以:

    这会在块执行时崩溃,如上所述

    httpDelegate.currentCallback = callback;
    

    这行得通

    MyCallbackType aCallback = callback;
    httpDelegate.currentCallback = aCallback;
    

    我会接受这个答案,如果有人有更多见解,我很乐意修改我的决定。 :)

    【讨论】:

      【解决方案3】:

      我认为那里发生的事情是 loginController 在调用其委托后立即死亡。因此发生崩溃。如果没有更多信息,我只能想到可能的情况:

      1. 块不保留 loginController 对象(__block 类型修饰符)。如果块是异步执行的,loginController 可能不再可用,如果它在 elsewere 中被杀死。因此,无论您想用它做什么,您都无法在块内访问它,并且应用程序将崩溃。如果控制器在发送 loginViewDidSubmit 后被终止,则可能会发生这种情况。

      2. 我认为这很可能是您的情况: loginController 调用其委托对象。委托方法最终会同步调用杀死控制器的回调块。调用委托方法后,控制器应该是活动的。在委托方法中杀死它,很可能会导致崩溃发生。为了确保这是问题所在,只需在委托方法中将 loginController 设为 nil,并在调用委托后将 NSLog 语句放入控制器中,别管这个块,你会在那里崩溃。

      也许如果您粘贴一些代码,我们可以提供更多帮助。

      我最好的。

      【讨论】:

      • 感谢您的拍摄,但不,loginController 始终可见。如果我取消对第二个 DLog() 的注释,我可以根据需要(从同一个 loginController 的视图)多次点击“登录”,并且我将始终获取日志,但尝试访问发送了信息。我只是想我的问题可能是其他问题,回调是从 NSURLConnectionDelegate 异步调用的。也许存在问题......将更新问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-23
      • 1970-01-01
      • 2011-04-09
      • 1970-01-01
      • 2013-10-03
      • 1970-01-01
      相关资源
      最近更新 更多