Blocks篇:7.循环引用问题
1.ARC下的Block循环引用
1.1 使用__weak或__unsafe_unretained避免循环引用
一般来说,最简单的这种情况即:block对象作为OC对象的成员,而在block函数体内部直接或间接捕获了该OC对象,造成互相强引用。举例来说:
typedef void (^MyBlock)();
@interface TestObject : NSObject {
// block成员
MyBlock myBlock;
}
@end
@implementation TestObject
- (instancetype)init {
if (self = [super init]) {
// 初始化block成员
myBlock = ^{
NSLog(@"%@", self);
};
}
return self;
}
@end
产生循环引用的过程:
- 由于myBlock对象捕获了OC对象(self),myBlock被自动拷贝到堆上,并自动对self产生强引用;
- myBlock是self的成员,当myBlock分配到堆内存后,self负责对其进行内存管理,自动对myBlock对象产生强引用;
- 互相保留,引用循环产生
不过这种情况,编译器可以直接识别并给出引用循环警告。我们可以通过所有权修饰符__weak或__unsafe_unretained,使myBlock对象通过保留弱引用或不安全的对象指针,来避免对self产生强引用:
- (instancetype)init {
if (self = [super init]) {
// 初始化block成员
__weak TestObject *weakSelf = self;
// __unsafe_unretained TestObject *unsafeSelf = self;
myBlock = ^{
NSLog(@"%@", weakSelf);
};
}
return self;
}
由于在init方法中不比担心self对象释放的问题,可以安心使用__unsafe_unretained修饰符。
1.2 通过__block变量,并适时将其释放来解除循环引用
@implementation TestObject
- (instancetype)init {
if (self = [super init]) {
// 初始化block成员
__block TestObject *blockSelf = self;
myBlock = ^{
NSLog(@"blockSelf - %@", blockSelf);
// 释放__block变量,其释放对self的强引用
blockSelf = nil;
};
// 通过执行block对象,在内部解除循环引用
myBlock();
}
return self;
}
- (void)dealloc {
NSLog(@"%@", NSStringFromSelector(_cmd));
}
由于在ARC下,复制到堆内存中的__block对象会对内部的OC对象进行强引用,故可以让block对象捕获__block对象,并在需要时释放__block对象(一般都是执行block时),以间接解除对self的强引用。
补充:
调用block后,直接将block对象释放也可以解除循环引用。
缺点:
一般来说,必须执行block后,引用循环才可以被解除,容易出现遗漏的情况。
2.非ARC下的Block内存控制
2.1 非ARC下的Block引用循环
由于非ARC环境下,无法使用所有权修饰符,故只能通过__block变量的方式来解决引用循环问题:
- (instancetype)init {
if (self = [super init]) {
// 初始化block成员
__block TestObject *blockSelf = self;
myBlock = ^{
NSLog(@"blockSelf - %@", blockSelf);
};
}
return self;
}
在上述代码中,直接使用__block对象即可,无需释放,即可避免生成引用循环。
原因:
非ARC环境下,__block变量被分配到堆内存时,不会对内部的OC对象进行强引用;
因此,在block对象函数内部,不会对OC对象发生强引用,相当于ARC下的__weak和__unsafe_unretained的情况。
2.2 非ARC环境下的Block内存使用规则补充
- 对不同内存区域的block执行retain和copy操作的结果:
block对象所在的内存区域 | 执行操作 | 效果 |
---|---|---|
栈 | retain | 无效 |
栈 | copy | 复制到堆内存 |
堆 | retain | 产生强引用 |
堆 | copy | 产生强引用 |
- 在C语言环境下,可以使用Block_copy()和Block_release()对堆内存的block对象进行内存管理。
网友评论