《Objective-C高級編程 iOS與OS X多線程與內(nèi)存管理》12

Blocks篇:3.Blocks使用捕獲到的變量

所謂Blocks捕獲變量,即在Block函數(shù)體內(nèi)使用外部聲明的變量。

1. 捕獲局部變量(自動變量)和靜態(tài)局部變量

// main.m
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int uselessVal = 1;
        // 局部變量
        int val = 3;
        // 靜態(tài)局部變量
        static int staticVal = 4;
        
        void (^myBlock)(void) = ^{
            // 捕獲val和staticVal使用
            printf("val = %d\n", val);
            printf("staticVal = %d\n", staticVal);
        };
        myBlock();
    }
    return 0;
}

轉(zhuǎn)換后的C++代碼部分如下:

// main.cpp

/** Block的完整結(jié)構(gòu)體聲明 */
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int val; // 內(nèi)部捕獲的局部變量
    int *staticVal; // 內(nèi)部捕獲的靜態(tài)局部變量
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int *_staticVal, int flags=0) : val(_val), staticVal(_staticVal) {
        impl.isa = &_NSConcreteStackBlock; // 這里只按照MRC編譯確定,ARC則不同
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

/** Block中函數(shù)體轉(zhuǎn)換生成的C函數(shù) */
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    // 獲取Block結(jié)構(gòu)體實例中捕獲的變量
    int val = __cself->val; // bound by copy
    int *staticVal = __cself->staticVal; // bound by copy
    printf("val = %d\n", val);
    printf("staticVal = %d\n", (*staticVal));
}


// main函數(shù)
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int uselessVal = 1;
        int val = 3;
        static int staticVal = 4;

        void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, val, &staticVal)); // 初始化時賦值,將要捕獲的變量傳遞進去
        ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    }
    return 0;
}

以上可以看出:

  1. 只有Block內(nèi)部使用的變量才會被捕獲;
  2. 普通的局部變量是通過值傳遞方式進行捕獲,靜態(tài)局部變量是通過指針傳遞方式進行的;
    • 原因:
      1. 由于C函數(shù)的聲明和實現(xiàn)是在原局部變量的生命周期之外(這里是main函數(shù)外部),使用時原變量早已被釋放,故需要直接將值保存到Block結(jié)構(gòu)體實例中;
      2. 對于靜態(tài)局部變量,由于靜態(tài)變量是存儲在專門的數(shù)據(jù)區(qū)(也就可以理解為全局),在C函數(shù)中也可以直接訪問,故只需將其指針保存在Block結(jié)構(gòu)體實例中即可。
  3. C函數(shù)內(nèi)部使用時,通過傳遞進去的Block指針取出被捕獲的變量值。
  4. 補充:在ARC環(huán)境下,對于Block的類型(isa)
    • 捕獲普通局部變量后,在運行時,系統(tǒng)會將Block拷貝到堆上,變?yōu)開NSConcreteMallocBlock
    • 捕獲靜態(tài)局部變量,雖然使用,實際也只是記錄該變量的指針,Block為_NSConcreteGlobalBlock
    • 沒有捕獲任何變量的Block為_NSConcreteGlobalBlock類型

2. 捕獲全局變量和全局靜態(tài)變量

// main.m

int globalVal = 3;
static int globalStaticVal = 4;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^myBlock)(void) = ^{
            printf("globalVal = %d\n", globalVal);
            printf("globalStaticVal = %d\n", globalStaticVal);
        };
        myBlock();
    }
    return 0;
}

轉(zhuǎn)換后的C++部分代碼為:

// main.cpp

int globalVal = 3;
static int globalStaticVal = 4;

/** Block完整結(jié)構(gòu)體聲明 */
struct __main_block_impl_0 {
    // 沒有保存全局變量
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    // 直接使用
    printf("globalVal = %d\n", globalVal);
    printf("globalStaticVal = %d\n", globalStaticVal);
}

可以看到,

  1. 對于全局變量(包含靜態(tài)全局變量),由于在Block的C函數(shù)中可以直接訪問,故無需在Block結(jié)構(gòu)中對其進行額外的保存工作
  2. 在ARC環(huán)境下,Block的類型(isa)為_NSConcreteGlobalBlock
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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