1、在使用block前需要對block指針做判空處理。
不判空直接使用,一旦指針爲空直接產生崩潰。
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);
}
}
2、在MRC的編譯環境下,block如果作爲成員參數要copy一下將棧上的block拷貝到堆上.
3、在block使用之後要對,block指針做賦空值處理,如果是MRC的編譯環境下,要先release掉block對象。
block作爲類對象的成員變量,使用block的人有可能用類對象參與block中的運算而產生循環引用。
將block賦值爲空,是解掉循環引用的重要方法。(不能只在dealloc裏面做賦空值操作,這樣已經產生的循環引用不會被破壞掉)
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會在棧上被回收。
}
- (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的指針所以這裏產生了循環引用,且由於這個block是作爲參數傳入對象的,編譯器不會報錯。
//因此,NetworkClass使用完block之後一定要將作爲成員變量的block賦空值。
if (netObj.propertyUseInCallBack == YES) {
//Do Something...
}
}];
}
@end
還有一種改法,在block接口設計時,將可能需要的變量作爲形參傳到block中,從設計上解決循環引用的問題。
如果上面Network類設計成這個樣子:
@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) {
//這裏參數中已經有netObj的對象了,使用者不用再從block外引用指針了。
if (aNetworkObj.propertyUseInCallBack == YES) {
//Do Something...
}
}];
}
@end
4、使用方將self或成員變量加入block之前要先將self變爲__weak
5、在多線程環境下(block中的weakSelf有可能被析構的情況下),需要先將self轉爲strong指針,避免在運行到某個關鍵步驟時self對象被析構。第4、第5條合起來有個名詞叫weak–strong dance,以下代碼來自AFNetworking,堪稱使用weak–strong dance的經典。
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
Review一下上面這段代碼,裏面玄機不少。
第一行:__weak __typeof(self)weakSelf = self;
如之前第4條所說,爲防止callback內部對self強引用,weak一下。
其中用到了__typeof(self),這裏涉及幾個知識點:
a. typeof、__typeof、typeof的區別
他們沒有區別,但是這牽扯一段往事,在早期C語言中沒有typeof這個關鍵字,typeof、__typeof是在C語言的擴展關鍵字的時候出現的。
typeof是現代GNU C++的關鍵字,從Objective-C的根源說,他其實來自於C語言,所以AFNetworking使用了繼承自C的關鍵字。
b.對於老的LLVM編譯器上面這句話會編譯報錯,所以在很早的ARC使用者中流行__typeof(&*self)這種寫法,原因如下
大致說法是老LLVM編譯器會將__typeof轉義爲 XXX類名 const __strong的__strong和前面的__weak關鍵字對指針的修飾又衝突了,所以加上&對指針的修飾。
第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
按照之前第5條的說法給轉回strong了,這裏__typeof()裏面寫的是weakSelf,裏面寫self也沒有問題,因爲typeof是編譯時確定變量類型,所以這裏寫self 不會被循環引用。
第四、五、六行,如果不轉成strongSelf而使用weakSelf,後面幾句話中,有可能在第四句執行之後self的對象可能被析構掉,然後後面的StausBlock沒有執行,導致邏輯錯誤。
最後第五行,使用前對block判空。