ARC:从委托方法中使用的内部块获取EXC_BAD_ACCESS

时间:2011-09-13 15:41:07

标签: cocoa-touch objective-c-blocks automatic-ref-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一直保持可见状态):

的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变量...

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

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


首先是“解决方案”:

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

这会在块执行时崩溃,如

所述
httpDelegate.currentCallback = callback;

这有效

MyCallbackType aCallback = callback;
httpDelegate.currentCallback = aCallback;

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

答案 2 :(得分:0)

我知道发生了什么,在调用其委托之后,loginController已经死了。因此发生崩溃。没有更多信息,我只能想到可能的情况:

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

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

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

    我最好。