1匀伏、在使用block前需要對block指針做判空處理荒辕。
不判空直接使用兼搏,一旦指針為空直接產(chǎn)生崩潰症虑。
復(fù)制代碼
if (!self.isOnlyNet) {
if (succBlock == NULL) { //后面使用block之前要先做判空處理
return;
}
id data = [NSKeyedUnarchiver unarchiveObjectWithFile:[self favoriteFile]];
if ([data isKindOfClass:[NSMutableArray class]]) {
succBlock(data,YES);
}else{
succBlock(nil,YES);
}
}
復(fù)制代碼
2膨俐、在MRC的編譯環(huán)境下勇皇,block如果作為成員參數(shù)要copy一下將棧上的block拷貝到堆上(示例見下,原因參考)
3焚刺、在block使用之后要對敛摘,block指針做賦空值處理,如果是MRC的編譯環(huán)境下乳愉,要先release掉block對象兄淫。
block作為類對象的成員變量,使用block的人有可能用類對象參與block中的運(yùn)算而產(chǎn)生循環(huán)引用蔓姚。
將block賦值為空捕虽,是解掉循環(huán)引用的重要方法。(不能只在dealloc里面做賦空值操作坡脐,這樣已經(jīng)產(chǎn)生的循環(huán)引用不會(huì)被破壞掉)
復(fù)制代碼
typedef void(^SuccBlock)(id data);
@interface NetworkClass {
SuccessBlock _sucBlock;
}
@property (nonatomic,assign)BOOL propertyUseInCallBack;
- (void) requestWithSucBlock: (SuccessBlock) callbackBlock;
@end
@implementation NetworkClass
- (void) requestWithSucBlock: (SuccessBlock) callbackBlock {
_sucBlock = callbackBlock;//MRC下:_sucBlock = [callbackBlock copy]; 不copy block會(huì)在棧上被回收泄私。
}
- (void) netwrokDataBack: (id) data {
if (data != nil && _sucBlock != NULL) {
_sucBlock(data);
}
//MRC下:要先將[_sucBlock release];(之前copy過)
_sucBlock = nil; //Importent: 在使用之后將Block賦空值,解引用 !!!
}
@end
//=======================以下是使用方===========================
@implementation UserCode
- (void) temporaryNetworkCall
{
NetworkClass *netObj = [[NetworkClass alloc] init];
netObj.propertyUseInCallBack = NO;
[netObj requestWithSucBlock: ^(id data) {
//由于block里面引用netObj的指針?biāo)赃@里產(chǎn)生了循環(huán)引用备闲,且由于這個(gè)block是作為參數(shù)傳入對象的晌端,編譯器不會(huì)報(bào)錯(cuò)。
//因此恬砂,NetworkClass使用完block之后一定要將作為成員變量的block賦空值咧纠。
if (netObj.propertyUseInCallBack == YES) {
//Do Something...
}
}];
}
@end
復(fù)制代碼
還有一種改法,在block接口設(shè)計(jì)時(shí)泻骤,將可能需要的變量作為形參傳到block中漆羔,從設(shè)計(jì)上解決循環(huán)引用的問題乳幸。
如果上面Network類設(shè)計(jì)成這個(gè)樣子:
復(fù)制代碼
@class NetowrkClass;
typedef void(^SuccBlock)(NetworkClass *aNetworkObj, id data);
@interface NetworkClass
//...
@end
@implementation NetworkClass
@end
@implementation UserCode
- (void) temporaryNetworkCall
{
NetworkClass *netObj = [[NetworkClass alloc] init];
netObj.propertyUseInCallBack = NO;
[netObj requestWithSucBlock: ^(NetworkClass *aNetworkObj, id data) {
//這里參數(shù)中已經(jīng)有netObj的對象了,使用者不用再從block外引用指針了钧椰。
if (aNetworkObj.propertyUseInCallBack == YES) {
//Do Something...
}
}];
}
@end
復(fù)制代碼
4粹断、使用方將self或成員變量加入block之前要先將self變?yōu)開_weak
5、在多線程環(huán)境下(block中的weakSelf有可能被析構(gòu)的情況下)嫡霞,需要先將self轉(zhuǎn)為strong指針瓶埋,避免在運(yùn)行到某個(gè)關(guān)鍵步驟時(shí)self對象被析構(gòu)。
第四诊沪、第五條合起來有個(gè)名詞叫weak–strong dance养筒,來自于2011 WWDC Session #322 (Objective-C Advancements in Depth)
以下代碼來自AFNetworking,堪稱使用weak–strong dance的經(jīng)典端姚。
復(fù)制代碼
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
復(fù)制代碼
Review一下上面這段代碼晕粪,里面玄機(jī)不少。
第一行:__weak __typeof(self)weakSelf = self;
如之前第四條所說渐裸,為防止callback內(nèi)部對self強(qiáng)引用巫湘,weak一下。
其中用到了__typeof(self)昏鹃,這里涉及幾個(gè)知識(shí)點(diǎn):
a. __typeof尚氛、__typeof__、typeof的區(qū)別
恩~~他們沒有區(qū)別洞渤,但是這牽扯一段往事阅嘶,在早期C語言中沒有typeof這個(gè)關(guān)鍵字,__typeof载迄、__typeof__是在C語言的擴(kuò)展關(guān)鍵字的時(shí)候出現(xiàn)的讯柔。
typeof是現(xiàn)代GNU C++的關(guān)鍵字,從Objective-C的根源說护昧,他其實(shí)來自于C語言魂迄,所以AFNetworking使用了繼承自C的關(guān)鍵字。
b.對于老的LLVM編譯器上面這句話會(huì)編譯報(bào)錯(cuò)捏卓,所以在很早的ARC使用者中流行__typeof(&*self)這種寫法极祸,原因如下
大致說法是老LLVM編譯器會(huì)將__typeof轉(zhuǎn)義為 XXX類名 *const __strong的__strong和前面的__weak關(guān)鍵字對指針的修飾又沖突了慈格,所以加上&*對指針的修飾怠晴。
第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
按照之前第五條的說法給轉(zhuǎn)回strong了,這里__typeof()里面寫的是weakSelf浴捆,里面寫self也沒有問題蒜田,因?yàn)閠ypeof是編譯時(shí)確定變量類型,所以這里寫self 不會(huì)被循環(huán)引用选泻。
第四冲粤、五美莫、六行,如果不轉(zhuǎn)成strongSelf而使用weakSelf梯捕,后面幾句話中厢呵,有可能在第四句執(zhí)行之后self的對象可能被析構(gòu)掉,然后后面的StausBlock沒有執(zhí)行傀顾,導(dǎo)致邏輯錯(cuò)誤襟铭。
最后第五行,使用前對block判空短曾。?