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 来建立ConnectionForURL。但是 httpConnectionFinishedWithError 包含对 self 的调用。当块被声明为内联时,对 self 的调用很好,但在如上所述完成时则不行,因为显然这会产生编译错误。

所以我想知道该块是否/如何成为调用类的块属性?(我之前已经使用块作为类的属性,就像在上面的 MYHTTPConnection 类中所做的那样,它工作正常,但是在这种情况下,我无法获得正确的语法来添加传递给的块作为属性建立连接ForURL调用类)。

或者,httpConnectionFinishedWithError 可以只调用另一个函数,但在那种情况下,我必须将 self 作为参数传递,在这种情况下,我将如何在块中获取 self ?

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

非常感谢

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

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

2 回答 2

2

如果你使用 ARC,你可以定义一个引用 self 的弱变量:

__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或创建一个属性/获取方法来访问实例变量。

如果您使用的是 ARC,但针对的是 iOS 4,则不能使用__weak,因为它不可用,也不能使用__block,因为它不会阻止块保留 ARC 下的变量。相反,您可以使用__unsafe_unretained. 请记住,如果变量引用的对象是dealloc'd ,__unsafe_unretained则无法解析......所以它是不安全的。nil你需要小心你如何使用它。例如,如果你传递另一个对象这个块self并被释放。对 _self 的任何引用都会导致错误。解决此问题的一种方法是创建一个内联“中间”块,该块将仅在需要该块时才保留 self 。例如,不要直接传递块,而是执行以下操作:

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

在上面的例子中,httpcompletionBlock是一个实例变量,所以selfget 被本地范围内的块捕获。您现在可以放心,self直到httpCompletionBlock执行完之后才会消失。您仍然避免了保留循环,因为只有内联块保留self,而不是存储块。

于 2012-05-24T19:12:40.333 回答
0

使用属性的另一种方法是从方法中返回块,如下所示:

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

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

没有保留周期,因为您的类没有保留块的实例。httpConnection回调后不会处理块并且您的对象会泄漏存在一些风险。如果您不拥有任何东西httpConnection,请确保您了解其语义并在 Instruments 中进行测试以确保它不会泄漏连接完成块。

于 2012-05-25T17:32:11.797 回答