Cocoa touch ARC:从委托方法中使用的内部块获取EXC_BAD_访问权限

Cocoa touch ARC:从委托方法中使用的内部块获取EXC_BAD_访问权限,cocoa-touch,objective-c-blocks,automatic-ref-counting,Cocoa Touch,Objective C Blocks,Automatic Ref Counting,我一定是做错了什么,但是自动引用计数文档并没有给我一个提示。我所做的是从委托方法内部使用块回调调用一个方法从块内部访问同一代理会导致访问错误。问题是我传递的对象-loginController正在向其委托发送消息-显然没有释放,当我在块内不访问它时,我可以多次调用该方法而不会出现问题。这是我的密码: - (void)loginViewDidSubmit:(MyLoginViewController *)loginController { NSString *user = loginCon

我一定是做错了什么,但是自动引用计数文档并没有给我一个提示。我所做的是从委托方法内部使用块回调调用一个方法从块内部访问同一代理会导致访问错误。问题是我传递的对象-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的可用堆栈跟踪。我做错了什么?谢谢你的指点


编辑:

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

流程(loginController始终可见):

登录控制器 视图代理 NSURLConnectionLegate
这就是我得到错误访问的地方,但只有当我访问loginController变量时…

我想发生的事情是,loginController在调用其委托后就死了。因此会发生崩溃。没有更多信息,我只能想到可能的场景:

  • 块不保留loginController对象(“块类型修改器”)。如果块是异步执行的,loginController可能不再可用,如果它被elsewere杀死。因此,无论你想用它做什么,你都无法在块内访问它,应用程序将崩溃。如果控制器在发送LoginViewIDSubmit后被终止,则可能发生这种情况

  • 我认为这很可能是您的情况:loginController调用其委托对象。委托方法最终同步调用终止控制器的回调块。在调用委托方法后,控制器应处于活动状态。在委托方法中杀死它,很可能会导致崩溃。要确保这是问题所在,只需在委托方法中禁用loginController,并在调用委托后在控制器中放入NSLog语句,更不用说块了,您将在那里遇到崩溃

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


    我的最佳选择。

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


    第一个“解决方案”:

    我刚刚找到了一种防止坏访问的方法。如我在上面的编辑中所示,视图委托将块转发给httpDelegate(另一个类的实例),httpDelegate反过来保持对块的强引用。无论出于何种原因,将块分配给临时变量并转发临时块变量都可以解决问题。因此:

    如前所述,这会在执行块时崩溃 这很有效
    我会接受这个答案,如果有人有更多的见解,我很乐意修改我的决定。:)

    将copy属性设置为属性,或者只调用块的“copy”方法

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

    感谢您的拍摄,但不,loginController始终可见。如果我取消对第二个DLog()的注释,我可以按我想要的次数点击“Login”(从同一个loginController的视图),我将始终获得日志,但我在尝试访问发送消息的控制器时遇到了错误的访问。我只是想我的问题可能是别的,回调是从NSURLConnectionLegate异步调用的。也许问题就在这里。。。将更新问题。这是正确的方法。事实上,我已将strong设置为属性属性,但在转换项目时,没有意识到该副本仍然可用。
    (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];
    
    - (void)connection:(NSURLConnection *)aConnection
      didFailWithError:(NSError *)error
    {
        if (NULL != currentCallback) {
            currentCallback([error localizedDescription]);
            self.currentCallback = NULL;
        }
    }
    
    httpDelegate.currentCallback = callback;
    
    MyCallbackType aCallback = callback;
    httpDelegate.currentCallback = aCallback;
    
    - (void)loginUser:(NSString *)user withPassword:(NSString *)pass callback:(void (^callback)(NSString *))
    {
        callback = [callback copy];