Block - Block本質(zhì)

學(xué)習(xí)小碼哥OC底層視頻筆記 - Block模塊
慢慢學(xué),總會成長的

1、生成代碼

  • 先定義一段代碼:
int main(int argc, const char * argv[]) {
    @autoreleasepool {
         // 定義一個簡單的block
        void (^block)(void) = ^{
            NSLog(@"block");
        };
        
        // 執(zhí)行block
        block();
    }
    return 0;
}
  • 將這段代碼轉(zhuǎn)成 C++ 代碼盲厌,打開終端署照,cd到對應(yīng)的main.c文件目錄中,輸入:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
  • main.cpp添加到項目中吗浩,為了編譯通過建芙,在編譯列表中取消main.cpp文件,具體操作:
取消main.cpp編譯.png

2懂扼、main.cpp 文件分析

現(xiàn)在提取main.cpp中主要內(nèi)容代碼岁钓,進行詳細分析

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
  • block的本質(zhì)結(jié)構(gòu)是一個結(jié)構(gòu)體
struct __main_block_impl_0 {
  struct __block_impl impl; // 包含邏輯代碼
  struct __main_block_desc_0* Desc; // 包含block大小
    
  // 構(gòu)造函數(shù)
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    // 此處block的類型是 _NSConcreteStackBlock,又一次證明block是對象
    impl.isa = &_NSConcreteStackBlock; 
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • __main_block_impl_0結(jié)構(gòu)體中impl的類型是__block_impl結(jié)構(gòu)體微王,由于結(jié)構(gòu)體地址等于其內(nèi)部第一個成員的地址屡限,所以__main_block_impl_0結(jié)構(gòu)體的地址為impl的地址,最后我們可以把__main_block_impl_0結(jié)構(gòu)體等價于:
struct __main_block_impl_0 {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
  struct __main_block_desc_0* Desc; 
    
  // 構(gòu)造函數(shù)
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    isa = &_NSConcreteStackBlock; 
    Flags = flags;
    FuncPtr = fp;
    Desc = desc;
  }
};
  • 構(gòu)造__main_block_impl_0結(jié)構(gòu)體炕倘,傳入的第一個參數(shù)
/// 執(zhí)行block內(nèi)部的邏輯代碼
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_96_0vbtlxxx6nd01n2n42jqy4xc0000gn_T_main_569e20_mi_0);
        }
  • 構(gòu)造__main_block_impl_0結(jié)構(gòu)體钧大,傳入的第二個參數(shù)
/// reserved = 0
/// Block_size = sizeof(struct __main_block_impl_0),即該block結(jié)構(gòu)體的大小
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
  • main.m文件中執(zhí)行代碼在main.cpp生成的代碼如下:
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
         // 定義一個簡單的block
        void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        
        // 執(zhí)行block
        ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    }
    return 0;
}
  • 由于小括號一般為強制轉(zhuǎn)換罩旋,此處為了好理解啊央,把小括號取消,做代碼簡化涨醋,簡化后:
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
        /**
         此處定義block變量
         說明:傳入兩個參數(shù)瓜饥,第一個是執(zhí)行邏輯代碼的函數(shù),第二個是block描述信息的地址
         
         再簡化:
         void (*block)(void) = &結(jié)構(gòu)體
         說明:指向的是結(jié)構(gòu)體的地址
         
         執(zhí)行說明:
         1浴骂、創(chuàng)建一個結(jié)構(gòu)體乓土,將代碼塊、block大小存入結(jié)構(gòu)體內(nèi)部溯警,再將該結(jié)構(gòu)體地址指給block變量
         2趣苏、結(jié)構(gòu)體內(nèi)部,由FuncPtr指針存儲函數(shù)梯轻,Des存儲描述內(nèi)容
         */
        void (*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
        
        /**
         執(zhí)行block內(nèi)部的代碼
 
         執(zhí)行過程:
         1食磕、由上面完整代碼可知,block被強制轉(zhuǎn)化為(__block_impl *)喳挑,原因是上面解釋過的彬伦,地址相同。
         2伊诵、__block_impl結(jié)構(gòu)體內(nèi)部有 void *FuncPtr 成員单绑,指向block內(nèi)部的執(zhí)行代碼,即 FuncPtr = __main_block_func_0日戈。
         3询张、__main_block_func_0執(zhí)行需要傳入block自己。根據(jù)__main_block_func_0函數(shù)可知浙炼。
         4份氧、直接調(diào)用FuncPtr指向的__main_block_func_0函數(shù)唯袄,執(zhí)行內(nèi)部邏輯代碼。
         */
        block->FuncPtr(block);
    }
    return 0;
}

3蜗帜、總結(jié)

  • 定義一個block恋拷,是生成了一個結(jié)構(gòu)體。block將邏輯代碼函數(shù)厅缺、block描述內(nèi)容作為參數(shù)蔬顾,傳入block結(jié)構(gòu)體的構(gòu)造函數(shù),FuncPtr存儲代碼塊湘捎,Desc存儲大小诀豁。

  • 執(zhí)行block,強轉(zhuǎn)類型后窥妇,直接調(diào)用FuncPtr變量舷胜,執(zhí)行代碼塊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末活翩,一起剝皮案震驚了整個濱河市烹骨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌材泄,老刑警劉巖沮焕,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拉宗,居然都是意外死亡峦树,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門簿废,熙熙樓的掌柜王于貴愁眉苦臉地迎上來空入,“玉大人络它,你說我怎么就攤上這事族檬。” “怎么了化戳?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵单料,是天一觀的道長。 經(jīng)常有香客問我点楼,道長扫尖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任掠廓,我火速辦了婚禮换怖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蟀瞧。我一直安慰自己沉颂,他們只是感情好条摸,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铸屉,像睡著了一般钉蒲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上彻坛,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天顷啼,我揣著相機與錄音,去河邊找鬼昌屉。 笑死钙蒙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的间驮。 我是一名探鬼主播仪搔,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蜻牢!你這毒婦竟也來了烤咧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤抢呆,失蹤者是張志新(化名)和其女友劉穎煮嫌,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抱虐,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡昌阿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了恳邀。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懦冰。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖谣沸,靈堂內(nèi)的尸體忽然破棺而出刷钢,到底是詐尸還是另有隱情,我是刑警寧澤乳附,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布内地,位于F島的核電站,受9級特大地震影響赋除,放射性物質(zhì)發(fā)生泄漏阱缓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一举农、第九天 我趴在偏房一處隱蔽的房頂上張望荆针。 院中可真熱鬧,春花似錦、人聲如沸航背。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沃粗。三九已至粥惧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間最盅,已是汗流浹背突雪。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涡贱,地道東北人咏删。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像问词,于是被迫代替她去往敵國和親督函。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容