Objective-C中的一个奇怪的块问题

时间:2013-05-18 11:39:17

标签: ios objective-c objective-c-blocks

我有一个AuthService类,它有一个方法来执行登录的异步连接。该类已实现NSURLConnectionDataDelegate协议,以便在服务器响应时,它调用先前由View Controller设置的完成处理程序来更新UI。

这是完成处理程序的定义

@property void (^completionHandler)(LoginResult *result);

这是班级收到服务器响应的时候

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSString *response = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];

    //Do something with the response and create an instance of LoginResult class

    self.completionHandler(loginResult);
}

如果完成处理程序块只是调用NSLog来向控制台写入作为参数传递的登录结果的信息,那么它运行完美且没有错误。但是当我想调用拥有该块的ViewController的方法时,就会发生一些奇怪的事情。

我知道当您在拥有该块的块中包含对象时,会有一个保留周期。所以这就是我编码的方式。

__block typeof(self) bself = self;

[authService login:blablabla completionHandler:^(LoginResult *result) {
    [bself didReceiveLoginResult:result];
}

我认为这会阻止进入保留周期。但调试时出现“Thread:EXC_BAD_ACESS”错误。

P.S。 例如,即使该属性未声明为“copy”

,以下代码也能完美运行
[authService login:blablabla completionHandler:^(LoginResult *result) {
    NSLog(@"Login %@", result.success ? @"success" : @"failed");
}

2 个答案:

答案 0 :(得分:4)

该属性应声明为copy,否则该块将保留在堆栈中,并且在您调用它时可以已取消分配。

此外,还有一些简单的方法可以防止保留圈。只需在使用它时释放该块,例如

self.completionHandler(loginResult);
self.completionHandler = nil;

没有__block的聪明魔法是必要的。暂时允许保留周期。

修改

如果块中没有对self的引用,编译器将使其成为全局块,并且不会被解除分配。见http://www.cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html

答案 1 :(得分:-1)

如果要在当前函数之外使用块,则需要复制块,因此在将其存储到属性中之前需要复制它:

- (void)setCompletionHandler:(void (^)(LoginResult *))handler {
    _completionHandler = [handler copy];
}

然后,当您在login:completionHandler:方法中分配完成处理程序时,它将被复制,然后存储在实例变量中。

这样,传递给函数的块将在存储到属性之前被复制,并且副本将位于堆上,而不是堆栈中,因此当它运行时它仍然存在后面。