Block 與 Closure

Block In OC

block 分為以下三種:

  • _NSConcreteStackBlock:棧block,引用了自動變量的block;
  • _NSConcreteMallocBlock:堆block,棧block執(zhí)行copy得到的block;
  • _NSConcreteGlobalBlock:全局block,沒有引用自動變量的block。

捕獲規(guī)則:

  1. 捕獲動作發(fā)生在創(chuàng)建block時;
  2. 對于全局變量(靜態(tài)/非靜態(tài)):在作用域中,不需要捕獲;
  3. 對于靜態(tài)變量(非全局):引用拷貝;
  4. 對于自動變量:值拷貝;
  5. 對于block變量(__block只能修飾自動變量):將變量包裝成為結(jié)構(gòu)體,引用拷貝該結(jié)構(gòu)體。

示例代碼:

typedef void(^Block)(void);

static int a = 1;

@implementation TestBlock

- (void)test {
    static int b = 1;
    int c = 1;
    __block int d = 1;
    NSObject *e = [NSObject new];
    __weak NSObject *f = e;
    __block NSObject *g = [NSObject new];
    
    Block block = ^{
        a = 2;
        b = 2;
        //c = 2; // error: Variable is not assignable
        printf("c = %d", c);
        d = 2;
        printf("e -> %p", e);
        printf("f -> %p", f);
        printf("f -> %p", g);
    };
    
    block();
}

@end

等效于下面的代碼:

typedef void(^Block)(void);

static int a = 1;

struct _struct_d { int d; };
struct _struct_g { NSObject *g; };

void _impl_block(int *b, const int c, struct _struct_d *d,
                 __strong NSObject *e, __weak NSObject *f,
                 struct _struct_g *g) {
    a = 2; // a為全局變量,可以直接訪問
    *b = 2; // b為靜態(tài)變量,引用拷貝
    //c = 2; //error
    printf("c = %d", c); // c為自動變量,值拷貝,并且拷貝后的變量為常量
    (*d).d = 2; // d為 __block 修飾的自動變量,將d包裝為 struct _struct_d *,然后拷貝該指針
    printf("e -> %p", e);
    printf("f -> %p", f);
    printf("f -> %p", g);
}

struct _struct_block {
    // 引用計數(shù)器。初始化之后引用計數(shù)為1;每增加一個引用時計數(shù)器+1;每減少一個引用時計數(shù)器-1;計數(shù)器為0時觸發(fā) free。
    int _retainCount; 
    int *b;
    const int c;
    struct _struct_d *d;
    __strong NSObject *e;
    __weak NSObject *f;
    struct _struct_g *g;
    void (* _impl_block)(int *, const int,
                         struct _struct_d *, __strong NSObject *,
                         __weak NSObject *, struct _struct_g *);
};

@implementation TestBlock

- (void)test {
    static int b = 1; 
    int c = 1;
    // 將d轉(zhuǎn)為指向堆區(qū)內(nèi)存的指針(該堆區(qū)內(nèi)存的管理未給出)
    struct _struct_d *d = (struct _struct_d *)malloc(sizeof(struct _struct_d));
    (*d).d = 1;
    __strong NSObject *e = [NSObject new];
    __weak NSObject *f = e;
    // 將g轉(zhuǎn)為指向堆區(qū)內(nèi)存的指針(該堆區(qū)內(nèi)存的管理未給出)
    struct _struct_g *g = (struct _struct_g *)malloc(sizeof(struct _struct_g *));
    (*g).g = [NSObject new];
    
    struct _struct_block block = { 
        1, // 初始化 block 后,引用計數(shù)為 1
        &b, // 靜態(tài)變量不會被釋放,引用拷貝即可
        c, // 普通變量,值拷貝
        d, // 指向堆區(qū)內(nèi)存的指針,值拷貝該指針即可
        e, // 指針,值拷貝;由于是 __strong 類型,對象的引用計數(shù)會增加
        f, // 指針,值拷貝;由于是 __weak 類型,對象的引用計數(shù)不變
        g, // 指針,值拷貝;拷貝 struct _struct_g 的指針不會影響到 NSObject 對象的引用計數(shù)
        _impl_block // 目標(biāo)函數(shù)
    }; // 拷貝變量,發(fā)生在創(chuàng)建 block 時;由于引用了自動變量,所以該 block 是棧 block。
    
    // 執(zhí)行 block,不需要從上下文中獲取變量
    block._impl_block(block.b, block.c, block.d, block.e, block.f, block.g); 
    
    block._retainCount --;
}

@end

總結(jié):

  1. 棧block的copy得到堆block,堆block和全局block的copy返回自身;
  2. 在ARC中,賦值操作會觸發(fā)棧block的copy,所以在ARC中大多數(shù)block是堆block;
  3. 在MRC中block捕獲的OC對象指針會觸發(fā)該對象的retain操作,防止出現(xiàn)這個現(xiàn)象的方法是:用__block修飾OC對象指針,將指針包裝為結(jié)構(gòu)體,copy操作不會復(fù)制該結(jié)構(gòu)體;
  4. 在ARC中block捕獲OC對象指針時,內(nèi)部的指針會繼承該指針的(__strong/__unsafe_unretained/__weak)修飾符,所以防止block強引用捕獲的OC對象的方法是:用__weak獲取一個弱引用OC對象的指針,在block使用__strong獲取一個強引用OC對象的指針,防止block執(zhí)行過程中OC對象被釋放。

將OC編譯為C++的指令:

clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations block_arc.m -o block_arc.cpp

Closure In Swift

捕獲規(guī)則:

  1. 捕獲動作發(fā)生在創(chuàng)建closure時,捕獲動作分為直接捕獲和列表捕獲;
  2. 直接捕獲:引用拷貝,類似OC中__block int__block NSObject *的做法;
  3. 列表捕獲非class:值拷貝,類似OC中int的做法;
  4. 列表捕獲class,拷貝對象的指針。根據(jù)使用的前綴不同,分為以下情況:
    • 沒有前綴:得到對象的__strong指針,對象引用計數(shù)+1;
    • unowned前綴:得到對象的__unsafe_unretained指針,對象引用計數(shù)不變;
    • weak前綴:得到對象的__weak指針,對象引用計數(shù)不變。

函數(shù)轉(zhuǎn)變?yōu)?closure:

函數(shù)被變量引用時,會自動轉(zhuǎn)變?yōu)殚]包,并且執(zhí)行捕獲動作。
如果函數(shù)是實例的成員函數(shù),除常規(guī)捕獲外,還會捕獲該實例。
這是容易引發(fā)循環(huán)引用的原因之一。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,621評論 1 32
  • 開始之前,我想先提幾個問題,看看大家是否對此有疑惑。唐巧已經(jīng)寫過一篇對block很有研究的文章,大家可以去看看(本...
    高思陽閱讀 1,820評論 0 1
  • 幽幽泮水薄采茆, 一碗莼羹勝佳肴。 陸游盛贊滑欲流, 東坡說得像個寶。 又見莼菜滿池漂, 輕舟泛水輕輕撈。 鈣鋅鎂...
    水果君s閱讀 2,004評論 0 0
  • 38歲極品男的征婚條件:要求膚白貌美,家務(wù)全包,年薪30萬,每月給出500塊錢生活費 對此,來看看網(wǎng)友們的評價
    百聞百味閱讀 288評論 0 0
  • 如果給你1萬元,讓你一年的時間內(nèi)寫文章要超過18萬字,你能做到嗎? 18萬字,這個龐大的數(shù)字可能會讓很多人決定放棄...
    那美喵星球閱讀 172評論 4 4

友情鏈接更多精彩內(nèi)容