2012-05-24 62 views
0

我已经创建了一个类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包含对自我的调用。当声明内联块时,对自我的调用是正确的,但在完成上述操作时不会发生这种情况,因为显然这会产生编译错误。

所以我想知道是否/如何块可以作为调用类的块属性? (我之前已经将块用作类的属性,正如在上面的MYHTTPConnection类中所做的那样,但在这种情况下,我无法获得将传递给establishConnectionForURL的块添加为属性的语法权限呼叫类)。

或者,httpConnectionFinishedWithError可以调用另一个函数,但在这种情况下,我不得不将自己作为参数传递,在这种情况下如何获得块内的自我?

还有其他更优雅的解决方案吗?

非常感谢

编辑: 更新,以显示实例编译错误的

void (^httpConnectionFinishedWithError)(NSError*) = 
^(NSError* error) 
{ 
    self.boolProperty = NO; // here 
} 
+0

«当块被声明为内联时,对自我的调用是正确的,但是当完成上述操作时不会发生这种情况,因为显然这会产生编译错误。»这根本不清楚。你在做什么工作?任何ObjC方法中都有一个有效的自我指针。如果你在类方法中,'self'指向类而不是实例 - 是问题所在? –

+0

是的,它需要访问实例属性和方法。 – Gruntcakes

+0

所以_你在做什么? –

回答

2

如果您使用ARC,您可以定义一个弱变量引用自:

__weak ClassOfSelf *_self = self;

并使用该块。如果你没有使用ARC,我相信你想使用__block而不是弱。在这两种情况下,该块不会保留自己,因此您可以避免保留周期。

我知道的存储/在课堂上使用块,最好的办法是先typedef定义它:

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,则不会直接引用块中的任何实例变量,因此不会创建保留周期。请记住引用_iVarself->_iVar相同。所以该块将捕获self。而是使用:_self->_iVar或创建一个property/get-method来访问实例变量。

如果您使用的是ARC,但是定位到iOS 4,则不能使用__weak,因为它不可用,并且您不能使用__block,因为它不会阻止该块将该变量保留在ARC下。相反,您可以使用__unsafe_unretained。只要记住__unsafe_unretained不会解析为nil如果该变量引用的对象是dealloc'd ...因此它是不安全的。你需要小心你如何使用它。例如,如果你传递另一个对象这个块并且self得到dealloc'd。任何对_self的引用都会导致错误。解决此问题的一种方法是创建一个内联“中间”块,只需要块就可以保留自己。例如,而不是直接传递块,做这样的事情:

self.httpConnection establishConnectionForURL: url andConnectionFinishedWithErrorBlock:^(NSError* error) 
      { 
       httpCompletionBlock(error); 
      }]; 

在上述情况下,httpcompletionBlock是实例变量,所以self获取的通过在局部范围内的取景拍摄。现在你放心,self不会消失,直到执行httpCompletionBlock之后。您仍然避免了一个保留循环,因为只有内联块保留self,而不是保存的块。

+0

谢谢,是的,我使用ARC,但也针对OS4。是否有任何向后兼容性问题需要考虑__weak? – Gruntcakes

+0

是的,有,我不相信__weak在iOS 4中可用。我会更新我的答案。 –

+0

好吧,我已经更新了使用ARC针对iOS 4平台的答案。 –

0

使用性能的替代方法是从方法返回块,像这样:

-(ConnectionFinishedWithErrorBlock)connectionFinishedWithErrorBlock { 
    return ^(NSError* error) { 
     self.boolProperty = NO; 
    }; 
} 

[self.httpConnection establishConnectionForURL:url 
      andConnectionFinishedWithErrorBlock:[self connectionFinishedWithErrorBlock]]; 

毫无保留周期,因为你的类没有握住块的一个实例。在回调后httpConnection不会处理该块,并且物体会泄漏。如果您不拥有任何httpConnection,请确保您了解它的语义,并在Instruments中测试以确保它不会泄漏连接完成的块。

+0

这看起来很整齐。 httpConnection是发布中的MYHTTPConnection类 – Gruntcakes

+0

因此,您应该确保MyHTTPConnection在使用后处理该块:if(self.completionBlock){self.completionBlock(error); self.completionBlock = nil;}' –

相关问题