28

使用 ARC 处理面向 4.0 和 5.0 的 iOS 项目。

遇到与块、ARC 和从块外部引用对象相关的问题。这是一些代码:

 __block AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
   [operation setCompletionBlock:^ {
       if ([operation isCancelled]) {
           return;
       }

... do stuff ...

operation = nil;
}];

在这种情况下,编译器会发出警告,在块中使用“操作”将导致保留循环。在 ARC 下,__block 现在保留了变量。

如果我添加 __unsafe_unretained,编译器会立即释放对象,所以显然这不起作用。

我的目标是 4.0,所以我不能使用 __weak。

我试着做这样的事情:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
__block __unsafe_unretained AFHTTPRequestOperation *weakOperation = operation;

但是虽然 weakOperation 不是 nil,但它的任何属性都不会在块内填充。

考虑到上面列出的项目限制,处理这种情况的最佳方法是什么?

4

2 回答 2

23

假设进度保证,保留周期可能正是您想要的。您在块结束时显式中断保留循环,因此它不是永久保留循环:调用块时,循环被中断。

但是,如果您有其他东西保持操作,您可以将引用存储到 a__weak__unsafe_unretained变量中,然后在您的块中使用它。__block除非您出于某种原因需要在块期间更改变量的绑定,否则无需对变量进行限定;由于您不再需要中断循环,因此您不需要为弱变量分配任何内容。

于 2011-10-13T22:42:38.757 回答
1

这似乎是 Conrad Stoll 在Blocks, Operations, and Retain Cycles中描述的问题,但他的文章遗漏了几个要点:

  • __block看起来像 Apple 推荐的避免在 MRC 模式下强引用捕获的变量的方法,但在 ARC 模式下完全没有必要。在这种情况下,ARC模式下是完全没有必要的;在 MRC 模式下也没有必要,尽管轻量级的解决方法更加冗长:void * unretainedOperation = operation; ... ^{ AFHTTPRequestOperation * op = unretainedOperation; }
  • 在 ARC 模式下,您需要一个强引用(因此您可以将其添加到队列中)和一个弱/unsafe_unretained 引用

最简单的解决方案如下所示:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
AFHTTPRequestOperation * __unsafe_unretained unretainedOperation = operation;

[operation setCompletionBlock:^ {
  if ([unretainedOperation isCancelled]) {
    return;
  }
  ... do stuff ...
}];

即使您打破了引用周期, Block 也没有理由AFHTTPRequestOperation首先保留 (假设操作在完成处理程序完成之前一直保持活动状态,这并不总是得到保证,但通常是正确的,并且由 ARC 假设,如果它被称为self进一步使用调用堆栈)。

最好的解决方法似乎是更新到最新的 AFNetworking,它将操作作为参数传递到块中。

于 2013-06-03T16:31:57.327 回答