Block 與 Closure

Block In OC

block 分為以下三種:

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

捕獲規(guī)則:

  1. 捕獲動(dòng)作發(fā)生在創(chuàng)建block時(shí);
  2. 對(duì)于全局變量(靜態(tài)/非靜態(tài)):在作用域中,不需要捕獲;
  3. 對(duì)于靜態(tài)變量(非全局):引用拷貝;
  4. 對(duì)于自動(dòng)變量:值拷貝;
  5. 對(duì)于block變量(__block只能修飾自動(dòng)變量):將變量包裝成為結(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òng)變量,值拷貝,并且拷貝后的變量為常量
    (*d).d = 2; // d為 __block 修飾的自動(dòng)變量,將d包裝為 struct _struct_d *,然后拷貝該指針
    printf("e -> %p", e);
    printf("f -> %p", f);
    printf("f -> %p", g);
}

struct _struct_block {
    // 引用計(jì)數(shù)器。初始化之后引用計(jì)數(shù)為1;每增加一個(gè)引用時(shí)計(jì)數(shù)器+1;每減少一個(gè)引用時(shí)計(jì)數(shù)器-1;計(jì)數(shù)器為0時(shí)觸發(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 后,引用計(jì)數(shù)為 1
        &b, // 靜態(tài)變量不會(huì)被釋放,引用拷貝即可
        c, // 普通變量,值拷貝
        d, // 指向堆區(qū)內(nèi)存的指針,值拷貝該指針即可
        e, // 指針,值拷貝;由于是 __strong 類型,對(duì)象的引用計(jì)數(shù)會(huì)增加
        f, // 指針,值拷貝;由于是 __weak 類型,對(duì)象的引用計(jì)數(shù)不變
        g, // 指針,值拷貝;拷貝 struct _struct_g 的指針不會(huì)影響到 NSObject 對(duì)象的引用計(jì)數(shù)
        _impl_block // 目標(biāo)函數(shù)
    }; // 拷貝變量,發(fā)生在創(chuàng)建 block 時(shí);由于引用了自動(dòng)變量,所以該 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中,賦值操作會(huì)觸發(fā)棧block的copy,所以在ARC中大多數(shù)block是堆block;
  3. 在MRC中block捕獲的OC對(duì)象指針會(huì)觸發(fā)該對(duì)象的retain操作,防止出現(xiàn)這個(gè)現(xiàn)象的方法是:用__block修飾OC對(duì)象指針,將指針包裝為結(jié)構(gòu)體,copy操作不會(huì)復(fù)制該結(jié)構(gòu)體;
  4. 在ARC中block捕獲OC對(duì)象指針時(shí),內(nèi)部的指針會(huì)繼承該指針的(__strong/__unsafe_unretained/__weak)修飾符,所以防止block強(qiáng)引用捕獲的OC對(duì)象的方法是:用__weak獲取一個(gè)弱引用OC對(duì)象的指針,在block使用__strong獲取一個(gè)強(qiáng)引用OC對(duì)象的指針,防止block執(zhí)行過程中OC對(duì)象被釋放。

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

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

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

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

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

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

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