我刚刚开始使用积木和Grand Central Dispatch。我被告知(并在Apple Documentation中阅读)任何从块中引用的对象都会被保留。
例如:
^{
self.layer.transform = CATransform3DScale(CATransform3DMakeTranslation(0, 0, 0), 1, 1, 1);
self.layer.opacity = 1;
}
“自我”被保留,因此泄漏。为了避免这种情况,我需要将self分配给:
__block Object *blockSelf = self;
然后在我的块中使用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];
}
答案 0 :(得分:48)
没有。当您的块保留一个保留它的对象时,会发生此问题。您的块将保留它引用的任何对象,但使用__block
注释的对象除外。因此:
// The following creates a retain cycle which will leak `self`:
self.block = ^{
[self something];
};
self
保留block
,block
隐含self
。这也将
如果您引用self
的实例变量,则会发生。
// The following avoids this retain cycle:
__block typeof(self) bself = self;
self.block = ^{
[bself something];
};
用__block
注释的变量是可变的(对于指针,即
他们指出的地址可以改变);结果,没有任何意义
保留该对象,因为您需要将该对象视为局部变量
(如,可以重新分配,影响块范围之外的对象)。因此,__block
不会被块保留。
但是,如果您尝试以某种方式使用此块,现在可能遇到无法预料的问题。例如,如果您决定以某种方式延迟调用此块,并且在执行该块时已释放self
,则程序将崩溃,因为您正在向已释放的对象发送消息。那么你需要的是一个弱引用,它不是在非垃圾收集环境中开箱即用的!
一种解决方案是使用MAZeroingWeakRef来包裹你的块;如果您在nil
被取消分配后尝试发送self
消息,那么这会将指针归零,以便您最终向self
发送消息:
MAZeroingWeakRef *ref = [MAZeroingWeakRef refWithTarget:self];
self.block = ^{
[ref.target something];
};
我还实现了weak reference wrapper in Objective-C++,它提供了更轻量级语法的好处:
js::weak_ref<SomeClass> ref = self;
self.block = ^{
[ref something];
};
因为js::weak_ref
是一个类模板,所以你会得到方便的强类型(也就是说,如果你尝试向引用发送一条它没有出现的消息,你会在编译时收到警告回应)。但迈克的MAZeroingWeakReference
比我的成熟得多,所以我建议使用他,除非你想弄脏你的手。
要详细了解__block
的问题以及弱引用的用例,请阅读Avoiding retain cycles with blocks, a right way和Jonathan Rentzsch's response。
答案 1 :(得分:2)
我会说这取决于你对你的阻止做什么。如果您没有将它存储在任何地方并在定义位置使用它(比如使用块对数组进行排序),那么它将与其中引用的变量一起释放(因为它是在堆栈中创建的并且标记为自动释放)。如果您将它存储在某个地方(数组,字典或可能将块传递给其他函数)copy
块,并在传递之前将其与autorelease
进行平衡。