我有一个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");
}
答案 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:
方法中分配完成处理程序时,它将被复制,然后存储在实例变量中。
这样,传递给函数的块将在存储到属性之前被复制,并且副本将位于堆上,而不是堆栈中,因此当它运行时它仍然存在后面。