我创建了一个类,它是NSURLConnection的包装器,类的用户传递在连接的各个阶段调用的块。包装类如下所示,简称为简洁:
typedef void (^ConnectionFinishedWithErrorBlock)(NSError* error);
@interface MYHTTPConnection : NSObject <NSURLConnectionDelegate>
@property (copy, nonatomic) ConnectionFinishedWithErrorBlock theErrorBlock;
@property (strong, nonatomic) NSURLConnection *connection;
- (void) establishConnectionForURL:(NSURL*) url
andConnectionFinishedWithErrorBlock: (ConnectionFinishedWithErrorBlock) errorBlock;
@end
到目前为止,我一直在使用内联声明的块,即
[self.httpConnection establishConnectionForURL: url
andConnectionFinishedWithErrorBlock:^(NSError* error)
{
...
}];
但是现在我的情况是我有一个非常长的错误处理块,并且将所有代码置于内联将变得混乱(因为API需要其他块未在此处显示)。
我知道我可以这样做:
void (^httpConnectionFinishedWithError)(NSError*) =
^(NSError* error)
{
}
然后将httpConnectionFinishedWithError传递给establishConnectionForURL。但httpConnectionFinishedWithError包含对self的调用。当块被内联声明时,对self的调用很好,但是当如上所述完成时则不然,因为这会产生编译错误。
所以我想知道是否/如何将块作为调用类的块属性? (我之前已经使用过块作为类的属性,就像在上面的MYHTTPConnection类中所做的那样工作正常,但是在这种情况下我无法正确地添加传递给establishConnectionForURL作为属性的块的语法。调用类)。
或者,httpConnectionFinishedWithError可以调用另一个函数,但是在那种情况下我必须将self作为参数传递,在这种情况下如何在块中获取self?
还有其他更优雅的解决方案吗?
非常感谢
编辑: 已更新以显示编译错误示例
void (^httpConnectionFinishedWithError)(NSError*) =
^(NSError* error)
{
self.boolProperty = NO; // here
}
答案 0 :(得分:2)
如果你正在使用ARC,你可以定义一个引用self的弱变量:
__weak ClassOfSelf *_self = self;
并在块中使用它。如果你不使用ARC我相信你想使用__block
而不是弱。在这两种情况下,块都不会保留自身,因此可以避免保留周期。
我知道在类中存储/使用块的最好方法是先键入它:
typedef void (^HttpConnectionFinishedWithError)(NSError*);
然后为块存储创建一个实例变量:
HttpConnectionFinishedWithError httpCompletionBlock;
在init中创建块并将其分配给实例变量。只需记住先复制块以便它移动到堆中(块在堆栈上创建,这意味着它们会在它们创建的范围消失后消失):
- (id) init{
if (self = [super init]){
__weak id _self = self; //or use class of self instead of id
httpCompletionBlock = [^(NSError *err){
.....code here, use _self instead of self
} copy];
}
return self;
}
(使用ARC的例子)
现在httpCompletionBlock
可以像在课堂上一样正常使用。只要您在块代码中使用_self
而不是self
并且,您不会直接引用块中的任何实例变量,就不会创建保留周期。请记住,引用_iVar
与self->_iVar
相同。因此该块将捕获self
。而是使用:_self->_iVar
或创建一个property / get-method来访问实例变量。
如果您使用的是ARC,但定位的是iOS 4,则无法使用__weak
,因为它不可用且您无法使用{{1}因为它不会阻止块在ARC下保留变量。相反,您可以使用__block
。请记住,如果变量引用的对象是dealloc,那么__unsafe_unretained
无法解析为__unsafe_unretained
,因此它不安全。你需要小心如何使用它。例如,如果您传递另一个对象此块,nil
将获得dealloc&#39; d。对_self的任何引用都会导致错误。解决这个问题的一种方法是创建一个内联&#34;中间&#34;只有块需要才能保留self的块。例如,不要直接传递块,而是执行以下操作:
self
在上面的例子中,self.httpConnection establishConnectionForURL: url andConnectionFinishedWithErrorBlock:^(NSError* error)
{
httpCompletionBlock(error);
}];
是一个实例变量,因此httpcompletionBlock
由本地范围内的块捕获。您现在确信self
在self
执行后才会消失。您仍然避免了保留周期,因为只有内联块保留httpCompletionBlock
,而不是存储块。
答案 1 :(得分:0)
使用属性的替代方法是从方法返回块,如下所示:
-(ConnectionFinishedWithErrorBlock)connectionFinishedWithErrorBlock {
return ^(NSError* error) {
self.boolProperty = NO;
};
}
[self.httpConnection establishConnectionForURL:url
andConnectionFinishedWithErrorBlock:[self connectionFinishedWithErrorBlock]];
没有保留周期,因为您的类没有保留块的实例。在回调之后httpConnection
将不会处理块并且您的对象将泄漏。如果你没有httpConnection
所拥有的,请确保你理解它的语义是什么,并在Instruments中测试,以确保它不会泄漏连接完成块。