iOS 4阻止并保留计数

sam*_*tte 19 iphone memory-management retain ios4 objective-c-blocks

我刚刚开始使用积木和Grand Central Dispatch.我被告知(并在Apple文档中阅读)任何块中引用的对象都会被保留.

例如:

^{  
    self.layer.transform = CATransform3DScale(CATransform3DMakeTranslation(0, 0, 0), 1, 1, 1);
    self.layer.opacity = 1;
}
Run Code Online (Sandbox Code Playgroud)

"自我"得到保留,因此泄漏.为了避免这种情况,我需要将self分配给:

__block Object *blockSelf = self;
Run Code Online (Sandbox Code Playgroud)

然后使用blockSelf而不是self在我的块中.

我的问题是:当你的块有更多的代码并引用几个对象时会发生什么?我需要将它们全部分配给__block对象吗?例如:

^{  
    [self doSomething];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"prevName == %@", artistName];
    [request setEntity:entity];
    [request setPredicate:predicate];

    Object *newObject = [[Object alloc] init];
    [someArray addObject];
    [newObject release];
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*ing 48

不会.当您的块保留一个保留它的对象时,会出现问题.您的块将保留它引用的任何对象,但带有注释的对象除外__block.因此:

// The following creates a retain cycle which will leak `self`:
self.block = ^{
  [self something];
};
Run Code Online (Sandbox Code Playgroud)

self保留block,并block隐含地保留self.如果您引用的实例变量也会发生这种情况self.

// The following avoids this retain cycle:
__block typeof(self) bself = self;
self.block = ^{
  [bself something];
};
Run Code Online (Sandbox Code Playgroud)

注释的变量__block是可变的(对于指针,也就是说,它们指向的地址可以改变); 因此,保留该对象是没有意义的,因为您需要将该对象视为局部变量(例如,它可以被重新分配,影响块范围之外的对象).因此,__block不要被块保留.

但是,如果您尝试以某种方式使用此块,现在可能遇到无法预料的问题.例如,如果您决定以某种方式延迟调用此块,并且self在执行该块时已经解除分配,则程序将崩溃,因为您正在向已释放的对象发送消息.那么你需要的是一个弱引用,它不是在非垃圾收集环境中开箱即用的!

一种解决方案是使用MAZeroingWeakRef来封装你的块; 这将使指针归零,这样如果您在解除分配后nil尝试发送消息,您将最终发送消息:selfself

MAZeroingWeakRef *ref = [MAZeroingWeakRef refWithTarget:self];
self.block = ^{
  [ref.target something];
};
Run Code Online (Sandbox Code Playgroud)

我还在Objective-C++中实现了一个弱引用包装器,它提供了更轻量级语法的好处:

js::weak_ref<SomeClass> ref = self;
self.block = ^{
  [ref something];
};
Run Code Online (Sandbox Code Playgroud)

因为js::weak_ref是一个类模板,所以你会得到方便的强类型(也就是说,如果你试图向引用发送一个它似乎没有响应的消息,你将在编译时收到警告).但迈克MAZeroingWeakReference比我的成熟得多,所以我建议使用他,除非你想弄脏你的手.

要阅读有关__block弱引用的问题和用例的更多信息,请阅读使用块避免保留周期,正确方法Jonathan Rentzsch的响应.