2

我有一个 AuthService 类,该类具有执行异步连接登录的方法。该类实现了 NSURLConnectionDataDelegate 协议,因此当服务器响应时,它会调用视图控制器先前设置的完成处理程序来更新 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];
}

我认为这将防止进入保留周期。但是调试时出现“线程:EXC_BAD_ACESS”错误。

PS 例如,即使该属性未声明为“复制”,以下代码也可以完美运行

[authService login:blablabla completionHandler:^(LoginResult *result) {
    NSLog(@"Login %@", result.success ? @"success" : @"failed");
}
4

2 回答 2

4

应该声明该属性,copy否则该块将保留在堆栈上,并且在您调用它时可能已经被释放。

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

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

不需要聪明的魔法__block。临时保留周期是允许的。

编辑:

如果块中没有引用self,编译器会将其设为全局块,并且永远不会被释放。见http://www.cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html

于 2013-05-18T11:48:34.417 回答
-1

如果您想在当前函数之外使用块,则需要复制它们,因此您需要先复制它,然后再将其存储到您的属性中:

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

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

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

于 2013-05-18T11:58:12.237 回答