天天看點

關于block使用的5點注意事項

轉自:搖滾詩人

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對象被析構。

第四、第五條合起來有個名詞叫weak–strong dance,來自于2011 WWDC Session #322 (Objective-C Advancements in Depth)

以下代碼來自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;

如之前第四條所說,為防止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;

按照之前第五條的說法給轉回strong了,這裡__typeof()裡面寫的是weakSelf,裡面寫self也沒有問題,因為typeof是編譯時确定變量類型,是以這裡寫self 不會被循環引用。

第四、五、六行,如果不轉成strongSelf而使用weakSelf,後面幾句話中,有可能在第四句執行之後self的對象可能被析構掉,然後後面的StausBlock沒有執行,導緻邏輯錯誤。

最後第五行,使用前對block判空。

寫在最後,閱讀好的開源庫源碼是提高個人水準的一個很好途徑,看見不懂的地方去查去摸索會得到更多。

參考:

http://blog.csdn.net/fhbystudy/article/details/17350951

http://stackoverflow.com/questions/10892361/generic-typeof-for-weak-self-references

http://stackoverflow.com/questions/14877415/difference-between-typeof-typeof-and-typeof-objective-c

http://rocry.com/2012/12/18/objective-c-type-of/

http://fuckingblocksyntax.com/ 這是個工具網站,我每次寫block的時候都會用到。