在ARC与非ARC环境下对block使用不当都会引起循环引用问题,一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是self.theBlock = ^(void){[self dosomething];或者self.otherVar = XXX;或者_otherVar = ...};block的这种循环引用会被编译器捕捉到并及时提醒,那要如何避免呢,我在这里做了个demo测试。
直接看demo
#import "ViewController.h"
#define CJSUPARC 1
typedef void(^myBlock)(void);
@interface ViewController ()
@property (nonatomic, copy)myBlock theBlock;
@end
@implementation ViewController
- (void)dealloc
{
#if CJSUPARC
[super dealloc];
#else
#endif
NSLog(@"ViewController dealloc");
}
- (void)viewDidLoad {
[super viewDidLoad];
#if CJSUPARC
__block typeof(self) wSelf = self;
NSLog(@"%@",@(self.retainCount));
myBlock block = ^(void){
NSLog(@"%@",@(wSelf.retainCount));
NSLog(@"%@.block init",[wSelf.class description]);
};
self.theBlock = [[block copy] autorelease];
if (self.theBlock) {
self.theBlock();
}
#else
// __block typeof(self) wSelf = self;
// __unsafe_unretained typeof(self) wSelf = self;
__weak typeof(self) wSelf = self;
myBlock block = ^(void){
NSLog(@"%@.block init",[wSelf.class description]);
};
self.theBlock = [block copy];
if (self.theBlock) {
self.theBlock();
}
#endif
}
@end
MRC模式下运行
2016-01-09 23:50:33.993 CJBlockDemo[52536:2193676] self.retainCount = 6
2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] self.retainCount = 6
2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
2016-01-09 23:50:40.437 CJBlockDemo[52536:2193676] ViewController dealloc
可以看到使用__block能够避免引起循环引用的问题ARC模式下运行
使用__block
2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
虽然编译器警告是没有了,但ViewController却没有执行dealloc函数,说明在arc中__block还是会引起retain。
使用__unsafe_unretained
使用__weak
2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
2016-01-09 23:50:40.437 CJBlockDemo[52536:2193676] ViewController dealloc
都可以避免循环引用的问题,但由于前者是unsafe的,会造成野指针问题,所以尽量少用unsafe_unretained关键字
另外在多线程环境下(block中的wSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。
可参考AFNetworking代码:
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
- 第一行:__weak __typeof(self)weakSelf = self;
为防止callback内部对self强引用,weak一下。
其中用到了__typeof(self),这里涉及几个知识点:
- __typeof、__typeof__、typeof的区别
恩~~他们没有区别,但是这牵扯一段往事,在早期C语言中没有typeof这个关键字,__typeof、__typeof__是在C语言的扩展关键字的时候出现的。
typeof是现代GNU C++的关键字,从Objective-C的根源说,他其实来自于C语言,所以AFNetworking使用了继承自C的关键字。 - 对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法,原因如下
大致说法是老LLVM编译器会将__typeof转义为 XXX类名 *const __strong的__strong和前面的__weak关键字对指针的修饰又冲突了,所以加上&*对指针的修饰。
第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
self转回strong了,这里__typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。
最后第五行,使用前对block判空。