ios - 在这个block中强捕获 'self'很可能会导致retain cycle

标签 ios objective-c automatic-ref-counting objective-c-blocks retain-cycle

<分区>

我有 block 请求。但是编译器发出警告

"Capturing 'self' strongly in this block is likely to lead to a retain cycle"

__weak typeof(self) weakSelf = self;
[generalInstaImage setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:data[@"images"][@"low_resolution"][@"url"]]] placeholderImage:[UIImage imageNamed:@"Default"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
    NSLog(@"success");
    [generalInstaImage setImage: image];
    [weakSelf saveImage:generalInstaImage.image withName:data[@"id"]];

    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
        NSLog(@"fail");
}];

我尝试像 weakSelf.generalInstaImage 这样编写示例,但是编译器会生成错误并且无法编译。

最佳答案

考虑这个警告:

Capturing self strongly in this block is likely to lead to a retain cycle

当您收到上述警告时,您应该检查您的阻止:

  • self 的任何显式引用;或
  • 任何因引用任何实例变量而引起的对 self 的隐式引用。

假设我们有一些简单的类属性,它是一个 block (这将遇到与您的问题相同的“保留循环”警告,但会使我的示例更简单一些):

@property (nonatomic, copy) void (^block)(void);

让我们假设我们有一些我们想在我们的 block 中使用的其他类属性:

@property (nonatomic, strong) NSString *someString;

如果您在 block 中引用 self(在我下面的示例中,在访问此属性的过程中),您显然会收到有关保留周期风险的警告:

self.block = ^{
    NSLog(@"%@", self.someString);
};

这是通过您建议的模式补救的,即:

__weak typeof(self) weakSelf = self;

self.block = ^{
    NSLog(@"%@", weakSelf.someString);
};

不太明显,如果您在 block 内引用类的实例变量,您还会收到“保留周期”警告,例如:

self.block = ^{
    NSLog(@"%@", _someString);
};

这是因为_someString 实例变量带有对self 的隐式引用,实际上等同于:

self.block = ^{
    NSLog(@"%@", self->_someString);
};

在这里,您可能也倾向于尝试采用弱 self 模式,但您做不到。如果您尝试 weakSelf->_someString 语法模式,编译器将警告您:

Dereferencing a __weak pointer is not allowed due to possible null value caused by race condition, assign it to strong variable first

因此,您可以通过使用 weakSelf 模式来解决这个问题,但也在 block 中创建一个本地 strong 变量并使用它来取消引用实例变量:

__weak typeof(self) weakSelf = self;

self.block = ^{
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        NSLog(@"%@", strongSelf->_someString);

        // or better, just use the property
        //
        // NSLog(@"%@", strongSelf.someString);
    }
};

顺便说一句,在 block 内创建本地 strong 引用 strongSelf 还有其他优点,即如果完成 block 异步运行一个不同的线程,您不必担心 self 在 block 执行时被释放,从而导致意想不到的后果。

这种 weakSelf/strongSelf 模式在处理 block 属性时非常有用并且你想防止循环引用(又名强引用循环),但同时确保self 不能在完成 block 的执行过程中被释放。

仅供引用,Apple 在 Use Lifetime Qualifiers to Avoid Strong Reference Cycles 的“非平凡循环”讨论中讨论了这种模式。 过渡到 ARC 发行说明的部分。


您报告在示例中引用 weakSelf.generalInstaImage 时收到了一些“错误”。这是解决此“保留周期”警告的正确方法,因此如果您收到一些警告,您应该与我们分享,并向我们展示您是如何声明该属性的。

关于ios - 在这个block中强捕获 'self'很可能会导致retain cycle,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17009966/

相关文章:

ios - 使用 AVCaptureSessionPreset 时如何更改图像质量

iphone - Objective C 类实例声明错误?

Swift 表示保留一个闭包参数

macos - 从 windowWillClose 上的 AppDelegate 中删除对 NSWindowController 的引用会导致崩溃

ios - 在用帧 0,0,0,0 初始化的 Storyboard 中创建的 ScrollView

ios - 如何重用 UITableView 中的单元格?

objective-c - Cocoa中窗口的位置

ios - UITapGestureRecognizer 中的 Objective c mkmapview 如何测试他们是否点击了注释

objective-c - 定义一个特定表格单元格的高度

objective-c - 在转换为 ARC 时,如何确保 Controller 在完成处理之前一直保留?