block的循环引用
block循环引用的例子:
例1:
typedef void (^blk_t)(void);
@interface MyObject : NSObject {
blk_t blk_;
}
@end
@implementation MyObject
- (id)init {
self = [super init];
blk_ = ^{NSLog(@"self = %@", self);};
return self;
}
- (void)dealloc {
NSLog(@"dealloc");
}
@end
例2:
@interface MyObject : NSObject {
blk_t blk_;
id obj_;
}
@end
@implementation MyObject
- (id)init {
self = [super init];
blk_ = ^{ NSLog(@"obj_ = %@", obj_); };
return self;
}
...
...
@end
分析:
两个例子中都是block语法赋值给了成员变量中,因此block语法生成的block从栈复制到堆,例1持有使用的self,例2持有成员变量间接持有self;因此self持有block,block持有self,这就形成了循环引用;
解决循环引用可以通过三种方式:__weak,__unsafe_unretained以及__block来修饰; 因此例1中加入id __weak tmp = self; 例2中加入:id __weak obj = _obj;并替换block中的self 和成员变量_obj即可;
而__block修饰避免循环引用的前提是block必须要执行,并且要在block 内将对象置为 nil ,否则还是会造成循环引用,因此可以通过 __block 变量去控制对象的生命周期;
typedef void (^blk_t)(void);
@interface MyObject : NSObject {
blk_t blk_;
}
@end
@implementation MyObject
- (id)init {
self = [super init];
__block id tmp = self;
blk_ = ^{
NSLog(@"self = %@", tmp);
tmp = nil;
};
return self;
}
- (void)execBlock {
blk_();
}
- (void)dealloc {
NSLog(@"dealloc");
}
@end
三种方式解决循环引用的原因:__weak和__unsafe_unretained修饰变量并不会持有对象,在不支持__weak的情况下可以使用__unsafe_unretained来修饰避免循环引用,而__block在 MRC 下,使用__block说明符也可以避免循环引用。因为当 block 从栈拷贝到堆时,__block对象类型的变量不会被 retain,没有 __block 说明符的对象类型的变量则会被 retian。正是由于 __block 在 ARC 和 MRC 下的巨大差异,我们在写代码时一定要区分清楚到底是 ARC 还是 MRC |