Grand Central Dispatch (GCD) 本身并不会造成循环引用。循环引用通常发生在两个对象相互持有对方的强引用时,或者当闭包(如 block)捕获了一个强引用,且被这个对象强引用时。在使用 GCD 时,你可能会创建一个 block 来执行某些任务,这个 block 可能会捕获一些外部变量。
循环引用的风险出现在以下情况:
- 一个对象强引用了一个 block(例如,将其保存为属性)。
- 这个 block 又直接或间接地强引用了这个对象。
在 GCD 中,你将 block 提交给一个 dispatch queue 来异步执行。通常情况下,这个 block 执行完毕后就会从队列中移除,不会造成循环引用。然而,如果在 block 内部捕获了一个强引用的 self,而 self 又强引用了这个 block(例如,作为一个属性或者是通过一个实例方法),那么就可能发生循环引用。
1. Dispatch Queues 持有 block:
如果你提交给 GCD 的 block 长时间没有完成(例如,在 block 中有一个强引用的无限循环),而且这个 block 捕获了 self,那么直到 block 执行完成,self 都不会被释放。
- (void)method {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
while (self.shouldContinue) { // self 被 block 捕获
// Infinite loop or long running task
}
});
}
2. Dispatch Groups:
如果你使用 dispatch group 监控一组任务,并且这些任务捕获了 self,而 self 又持有这个 group 或者其中的 block,这也可能导致循环引用。
- (void)method {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
self.myBlock = ^{
// Do something
dispatch_group_leave(group);
};
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 这个 block 可能捕获了 self
[self doSomethingWhenGroupFinished];
});
}
为了避免循环引用,你应该使用弱引用(__weak
)或无主引用(__unsafe_unretained
)来捕获涉及到的对象,尤其是在 block 内部。如果需要在 block 内部使用这些对象,你可以在 block 内部将弱引用转换为强引用,以确保在 block 执行期间对象不会被释放。这种模式称为“弱强舞”。
__weak typeof(self) weakSelf = self;
self.myBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomething];
}
};
通过这种方式,你可以确保即使 block 捕获了 self,也不会形成循环引用,因为 weakSelf 不会增加 self 的引用计数。当 block 执行完毕后,即使它被 self 强引用,也不会阻止 self 被释放。
3. Block 作为 GCD 任务被提交,内部捕获 self
,且 self
持有这个任务
@implementation MyClass
- (void)configureBlock {
// self 持有 dispatch block
dispatch_block_t block = ^{
// block 捕获了 self
[self doSomething];
};
self.myBlock = block;
// 提交 block 到 GCD
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}
- (void)doSomething {
// ...
}
@end
在这个例子中,即使 MyClass
实例发布一个 block 到 GCD,但由于 self
也持有这个 block,这依然可能会导致循环引用。
4. 避免 GCD 循环引用的方法
要避免这些循环引用,可以使用弱引用/无主引用来捕获 self
:
@implementation MyClass
- (void)configureBlock {
__weak typeof(self) weakSelf = self; // 创建一个弱引用的 self
self.myBlock = ^{
__strong typeof(weakSelf) strongSelf = weakSelf; // 在 block 内部将弱引用转化为强引用
[strongSelf doSomething];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), self.myBlock);
}
- (void)doSomething {
// ...
}
@end
通过这种方式,block 不会持有 self
的强引用。当 block 执行时,它会尝试将弱引用的 self
转换为强引用的 strongSelf
,确保在 block 执行期间 self
不会被释放。这样可以避免循环引用,同时确保 self
在 block 执行时仍然存在。
记住,循环引用不仅仅和 GCD 有关,它与 block 的捕获特性有关,无论 block 是在 GCD、NSTimer、还是任何其他异步或延时执行的环境中使用