在ARC與非ARC環(huán)境下對block使用不當都會引起循環(huán)引用問題,一般表現(xiàn)為,某個類將block作為自己的屬性變量,然后該類在block的方法體里面又使用了該類本身,簡單說就是self.theBlock = ^(void){[self dosomething];或者self.otherVar = XXX;或者_otherVar = ...};block的這種循環(huán)引用會被編譯器捕捉到并及時提醒,那要如何避免呢,我在這里做了個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能夠避免引起循環(huán)引用的問題ARC模式下運行
使用__block
2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
雖然編譯器警告是沒有了,但ViewController卻沒有執(zhí)行dealloc函數(shù),說明在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
都可以避免循環(huán)引用的問題,但由于前者是unsafe的,會造成野指針問題,所以盡量少用unsafe_unretained關鍵字
另外在多線程環(huán)境下(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的區(qū)別
恩~~他們沒有區(qū)別,但是這牽扯一段往事,在早期C語言中沒有typeof這個關鍵字,__typeof、__typeof__是在C語言的擴展關鍵字的時候出現(xiàn)的。
typeof是現(xiàn)代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 不會被循環(huán)引用。第四、五、六行,如果不轉成strongSelf而使用weakSelf,后面幾句話中,有可能在第四句執(zhí)行之后self的對象可能被析構掉,然后后面的StausBlock沒有執(zhí)行,導致邏輯錯誤。
最后第五行,使用前對block判空。