【问题标题】:ARC, Blocks and Retain CyclesARC、块和保留周期
【发布时间】:2011-12-07 08:18:05
【问题描述】:

使用 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,但它的任何属性都不会在块内填充。

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

【问题讨论】:

    标签: iphone ios ios5 automatic-ref-counting afnetworking


    【解决方案1】:

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

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

    【讨论】:

    • 我脑子里已经有了“不保留周期”的想法,我什至没有像你描述的那样考虑它。呃。下一个问题 - 有什么方法可以使编译器警告静音?这会让我发疯的。
    • 参见 Clang 用户手册中的 "Controlling Diagnostics via Pragmas"。您只需要弄清楚要忽略哪个警告标志。
    • 作者是#pragma clang diagnostic ignored "-Warc-retain-cycles"
    • 对不起,我知道我迟到了,但密切关注@JeremyW.Sherman 的开场白(“假设进度保证”)很重要,尤其是 AFNetworking,因为这是不是的情况。在您的示例中,如果操作被取消,您将在将操作设置为 nil 之前返回,并且类似地在 AFHTTPRequestOperation.m:setCompletionBlockWithSuccess:... 如果操作被取消,则不会调用完成或错误块,从而保留您的操作。
    • 即使假设有进度保证,参考周期通常也不是“你想要的”,尽管它可能是可以接受的。
    【解决方案2】:

    这似乎是 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(假设操作在完成处理程序完成之前一直保持活动状态,这是'并不总是保证,但如果在调用堆栈的更上方使用self 引用它,则通常是正确的并由 ARC 假定)。

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

    【讨论】:

    • __block 并非完全没有必要。默认情况下,block 保留的所有变量都将在其中 const ,因此您无法更改它们的 values 。 __block 来救援了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-04
    • 1970-01-01
    相关资源
    最近更新 更多