1. 程式人生 > >block使用時的一些情況以及防止迴圈引用

block使用時的一些情況以及防止迴圈引用

block 是在 iOS 4 中引入的新特性,蘋果官方推薦使用這種做法。

block 注意事項

1,block 在實現時就會對它引用到的它所在方法中定義的棧變數進行一次只讀拷貝,然後在 block 塊內使用該只讀拷貝。

如下程式碼:

-(void)testVariable
{
    NSInteger outsideVariable = 10;
    //__block NSInteger outsideVariable = 10;
    NSMutableArray * outsideArray = [[NSMutableArray alloc] init];
    void (^blockObject)(void
) = ^(void){ NSInteger insideVariable = 20; NSLog(@" > member variable = %d", self.memberVariable); NSLog(@" > outside variable = %ld", (long)outsideVariable); NSLog(@" > inside variable = %ld", (long)insideVariable); [outsideArray addObject:@"AddedInsideBlock"
]; }; outsideVariable = 30; self.memberVariable = 30; blockObject(); NSLog(@" > %lu items in outsideArray", (unsigned long)[outsideArray count]);}

列印結果如下:這裡寫圖片描述
注意到outside 變數的輸出值為10,這是因為blockObject 在實現時會對 outside 變數進行只讀拷貝,在 block 塊內使用該只讀拷貝。因此這裡輸出的是拷貝時的變數值 10。如果,我們想要讓 blockObject 修改或同步使用 outside 變數就需要用 __block 來修飾 outside 變數。

**注意**

a),在上面的 block 中,我們往 outsideArray 陣列中添加了值,但並未修改 outsideArray 自身,這是允許的,因為拷貝的是 outsideArray 自身。

b),對於 static 變數,全域性變數,在 block 中是有讀寫許可權的,因為在 block 的內部實現中,拷貝的是指向這些變數的指標。

c), __block 變數的內部實現要複雜許多,__block 變數其實是一個結構體物件,拷貝的是指向該結構體物件的指標。

2 ,使用 weak–strong dance 技術來避免迴圈引用
在block中呼叫self很可能會引起迴圈引用,在block中需要對weakSelf進行strong,保證程式碼在執行到block中,self不會被釋放,當block執行完後,會自動釋放該strongSelf。

先看這個例子:

NSArray *anArray = @[@"1", @"2", @"3"];
[anArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [self doSomething:idx];
}];

這種情況下,block中retain了self,當block中的程式碼被執行完後,self就會被ARC釋放。所以不需要處理weakself的情況。
該VC被銷燬後會有delloc列印。

再來看一個例子:

  NSArray *anArray = @[@"1", @"2", @"3"];
    [anArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [self dosomething:obj];
    }];
//    __weak SecondViewController *weakself = self;
    self.aBlock = ^(id obj, NSUInteger idx, BOOL *stop){
        [self dosomething:obj];
    };

這種情況編譯器會包警告提示這兒的self:
這裡寫圖片描述
並且不會有delloc的列印。
這個例子的區別在於:block被self strong引用。所以結果就是block中引用了self,self引用了block。那麼這個時候,如果你不使用weakself,則self和block永遠都不會被釋放。

那麼是不是遇到block都要使用weakself呢?當然不是,而且如果全部使用weakself,會出現你想執行block中的程式碼時,self已經被釋放掉了的情況。

另外,在處理weakself時,有兩種做法:__weak和__unsafe_unretained。兩種做法各有推薦,

還有一種比較常用的方法,這個方法比較通用:

/**
* 強弱引用轉換,用於解決程式碼塊(block)與強引用self之間的迴圈引用問題
* 呼叫方式: `@weakify_self`實現弱引用轉換,`@strongify_self`實現強引用轉換
*
* 示例:
* @weakify_self
* [obj block:^{
* @strongify_self
* self.property = something;
* }];
*/
#ifndef    weakify_self
#if __has_feature(objc_arc)
#define weakify_self autoreleasepool{} __weak __typeof__(self) weakSelf = self;
#else
#define weakify_self autoreleasepool{} __block __typeof__(self) blockSelf = self;
#endif
#endif
#ifndef    strongify_self
#if __has_feature(objc_arc)
#define strongify_self try{} @finally{} __typeof__(weakSelf) self = weakSelf;
#else
#define strongify_self try{} @finally{} __typeof__(blockSelf) self = blockSelf;
#endif
#endif
/**
* 強弱引用轉換,用於解決程式碼塊(block)與強引用物件之間的迴圈引用問題
* 呼叫方式: `@weakify(object)`實現弱引用轉換,`@strongify(object)`實現強引用轉換

用的時候就:
@weakify(self) 外部weak
@strongify(self) 內部strong

這樣就避免了迴圈引用了,dealloc列印如下:

2016-05-18 11:36:31.531 lianxi[10219:1055254] *****
dealloc
No retain cycle