iOS块 - 避免保留周期

时间:2015-04-28 11:50:58

标签: ios objective-c-blocks retain-cycle

如果我在一个区块内使用dispatch_queue怎么办?什么是避免保留周期的正确方法,但也避免过早释放弱指针?

    __weak MyClass *weakSelf = self;

    [apiClient fetchData:^(...) {
        typeof(self) selfref = weakSelf;

        dispatch_async(dispatch_get_main_queue(), ^{
            // using selfref here
        }
    });

这是正确的做法还是我错过了什么?如何确保正确处理所有内容并且不会发生保留周期?我不能在这里做一些dealloc测试...

我的问题与this here不同,因为我在第一个区块内有另一个区块。我问的是如何处理这种情况。

3 个答案:

答案 0 :(得分:8)

首先澄清一下,只有当对象指向自身时才会出现保留周期!这通常发生在对象的某些属性具有强引用对象(obj持有属性,其中包含对象...)时,无论是通过块还是委托。

Blocks捕获对从外部作用域传递给它们的任何对象的强引用 - 包括self。当你声明一个块并执行它时,这不是问题,因为块是在方法中声明的,而ARC是将在方法结束时解除它:

-(void)test
{
    void(^aBlock)() = ^{

        [self someMethod];

    };

    aBlock();
}

但是当您将此块存储到以后的某些用法(例如回调)时会出现问题,如下所示:

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

-(void)test
{
    _aBlock = ^{

        [self someMethod];

    };
}

我们现在在ClassA中有一个强引用(我们的属性aBlock),它对ClassA有强引用(在aBlock实现中通过self)。现在我们遇到了一个问题 - 这个类指向自己,永远不会被释放!如果我们按如下方式重写,那就更加明显了:

-(void)test
{
    self.aBlock = ^{

        [self someMethod];

    };
}

现在让我们打破你的代码只是为了确保我们正在谈论相同的事情:

__weak MyClass *weakSelf = self;

[apiClient fetchData:^(...) {
    typeof(self) selfref = weakSelf;  <---- outer block

    dispatch_async(dispatch_get_main_queue(), ^{
        // using selfref here   <---- inner block
    }
                   });

一步一步走:

  1. 我们声明weakSelf,所以当在外部块中使用时,它不会存储对自己的强引用 - retainCount保持不变
  2. 我们确保weakSelf在外部块结束之前可用,通过局部强变量捕获它(如果你感兴趣为什么会发生这种情况,请注意自我既不弱也不强 - 它不安全_不可恢复) 。此外部强变量将在外部范围结束时取消引用。所以它只在外部块开始时 +1,在结束时为-1 - 在块之外没有变化。
  3. 内部块将捕获强引用本地强引用(+1),但内部块本身在外部块中声明(将其视为变量,或先查看&#39) ;测试&#39;场景),因此它将在外部块的末尾安全地解除分配。释放内部块将导致对其持有的本地强变量的引用的安全dealloc,因此 -1保留计数 - 再次在块范围之外没有变化。
  4. 希望这一切对你有意义,并且你将与ObjC块一起光荣地战斗;)

答案 1 :(得分:0)

据我所知,当你使用block时,你不应该使用强参考。但如果你需要&#34; self&#34;在您的代码中,您可以像scanerio一样声明弱属性。我认为,你的代码工作得很好。

为了确保,您可以轻松地测试覆盖dealloc方法kust,如下所示。

-(void)dealloc{
    NSLog(@"Deallocated");
}

请复制并粘贴下面的代码段,然后运行您的代码。如果你看到&#34; Deallocated&#34;您的控制台中的单词,您没有保留周期。您也可以通过个人资料观看保留周期。

答案 2 :(得分:-1)

使用以下代码管理block的最佳方式。

 __weak MyClass *weakSelf = self;

    [apiClient fetchData:^(...) {
        __strong MyClass * selfref = weakSelf;

        dispatch_async(dispatch_get_main_queue(), ^{
            // using selfref here
        }
    });

这里__strong是为了保证当weakSelf在执行块的第一行时仍处于活动状态时,它将继续存在以执行块的其余部分。所以如果你在另一个block内使用该变量,那就不会有任何问题。因为main block范围在inner block执行时完成。

此处typeof(self) selfref = weakSelf;等于__strong MyClass * selfref = weakSelf;

希望这对你有所帮助。