iOS Block Part4:block捕獲__block基本數據類型的編譯結果分析

1. __block修飾基礎類型

int main(int argc, const char * argv[]) {
__block int any = 1;
 void (^test)() = ^ {
        NSLog(@"%d",any);
 };
 test();
 return 0;
}
}

老規(guī)矩顯示編譯結果,后是示意圖:
編譯結果:


block(__block int)_code.png

結構示意圖:


block(__block int)_map.png
block(int)

struct __main_block_impl_0 {
 struct __block_impl impl;//block方法實現
 struct __main_block_desc_0* Desc;//block的描述
 int any;//block捕獲了外部變量
};
block(__block int)

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 __Block_byref_any_0 *any;
}

對比捕獲int與捕獲__block intblock編譯生成的struct __main_block_impl_0.
int any變成了__Block_byref_any_0 *any.
結構如下:

struct __Block_byref_any_0 {
    void *__isa;
    __Block_byref_any_0 *__forwarding;
 int __flags;
 int __size;
 int any;
};

struct __Block_byref_any_0也就是實現__block修飾變量可以在block內外同呼吸,共患難的關鍵.

當然還可以注意到:

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
}

struct __main_block_desc_0也有拷貝和銷毀的方法.
這個拷貝的方法是供堆上struct __Block_byref_any_0拷貝用.
當然這個銷毀方法也是供堆上struct __Block_byref_any_0銷毀用的.

2. __block如何實現同呼吸,共患難

2.1 首先我們看看不帶__block,外圍變量與內部的是什么關系吧.

回看int any:

 int any = 1;
 void (*test)() = (
                   (void (*)())&__main_block_impl_0(
                                                    (void *)__main_block_func_0,
                                                    &__main_block_desc_0_DATA,
                                                    any
                                                     )
                 );
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _any, int flags=0) : any(_any) {
  impl.isa = &_NSConcreteStackBlock;
  impl.Flags = flags;
  impl.FuncPtr = fp;
  Desc = desc;
 }

1.生成 棧上的struct __main_block_impl_0
在調用__main_block_impl_0函數時,傳入any,注意__main_block_impl_0函數末尾的any(_any),完成了_any=any,一個最簡單的C語言函數調用就完成了,生成一個struct __main_block_impl_0.

2.拷貝 棧上的struct __main_block_impl_0到堆上
前面已經說過棧上的struct __main_block_impl_0會被完整的拷貝到堆上.

block外圍的int any <值等于> 棧上struct __main_block_impl_0->int any
棧上struct __main_block_impl_0->int any <值等于> 堆上struct __main_block_impl_0->int any

所以不用__block修飾基本數據類型的變量,block內外用的變量的關系就是C語言函數調用時實參與形參的關系,其中一方值的改變對于對方沒有任何影響.

不用__block修飾的對象數據類型的變量也是同理,就不做贅述了

2.2 帶__block,外圍變量與內部變量的是什么關系呢

回看__block int:

__attribute__((__blocks__(byref))) __Block_byref_any_0 any = {
                                                            (void*)0,
                                                            (__Block_byref_any_0 *)&any,
                                                            0,
                                                            sizeof(__Block_byref_any_0),
                                                            1
                                                               };
 void (*test)() = (
       (void (*)())&__main_block_impl_0(
             (void *)__main_block_func_0,
             &__main_block_desc_0_DATA,
             (__Block_byref_any_0 *)&any,
             570425344
             )
       );
struct __Block_byref_any_0 {
 void *__isa;
 __Block_byref_any_0 *__forwarding;
 int __flags;
 int __size;
 int any;
};

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 __Block_byref_any_0 *any; // by ref
 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_any_0 *_any, int flags=0) : any(_any->__forwarding) {
  impl.isa = &_NSConcreteStackBlock;
  impl.Flags = flags;
  impl.FuncPtr = fp;
  Desc = desc;
 }
};

可以看到,因為__block的作用,int any被轉化為__Block_byref_any_0 any
而且在__main_block_impl_0調用時傳入的是__Block_byref_any_0 any結構體的地址

__Block_byref_any_0 * _any是形參,
any(_any->__forwarding)完成了_any->__forwarding指向了實參__Block_byref_any_0 * any

在看block內部使用到變量,

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
 __Block_byref_any_0 *any = __cself->any; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_qp_p2pj3jmj65n39jgl4wx9_l9w0000gn_T_main_ad101a_mi_0,(any->__forwarding->any));
}

在看block外使用到變量,

NSLog((NSString *)&__NSConstantStringImpl__var_folders_qp_p2pj3jmj65n39jgl4wx9_l9w0000gn_T_main_d1ce15_mi_1,(any.__forwarding->any));

ps:關于block外使用到變量,圖片block(__block int)_code所展示的編譯結果是沒有block外使用到變量.
block外使用到變量代碼如下,筆者就不再編譯貼出結果了,如果對block外使用到變量有異議可以用如下代碼編譯看看.

int main(int argc, const char * argv[]) {
__block int any = 1;
 void (^test)() = ^ {
        NSLog(@"%d",any);//block內使用到變量
 };
 test();
NSLog(@"%d",any);//block外使用到變量
 return 0;
}
}

可以看到,自__block int any之后所有在block外用到any的地方都被換成了(any->__forwarding->any)

(any->__forwarding->any)要繞明白,前一個any是一個struct __Block_byref_any_0,后一個any是struct __Block_byref_any_0內的int any

1.生成 棧上的struct __main_block_impl_0

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 __Block_byref_any_0 *any;
}
struct __Block_byref_any_0 {
 void *__isa;
 __Block_byref_any_0 *__forwarding;
 int __flags;
 int __size;
 int any;
};

注意此時struct __Block_byref_any_0__forwarding的指向 看圖:

stack_forwarding.jpg

此時struct __Block_byref_any_0__forwarding指向自己本身.

2.拷貝 棧上的struct __main_block_impl_0拷貝到堆上

一個struct __main_block_impl_0在棧上,
一個struct __main_block_impl_0在堆上.

看圖:

heap_forwarding.jpg

回到這個式子(any->__forwarding->any),前一個any一定是"棧上的__Block_byref_any_0結構體any

經過拷貝生成堆block的同時,
棧上的struct __main_block_impl_0 any->__forwarding指向了堆上的struct __main_block_impl_0 any,
堆上的struct __main_block_impl_0 any->__forwarding指向自己.

block內:堆上的struct __main_block_impl_0 any->__forwarding->int any ==> 堆上struct __main_block_impl_0 any->int any;
block外:棧上的struct __main_block_impl_0 any->__forwarding->int any ==> 堆上struct __main_block_impl_0 any->int any;

一模一樣,又是同一個地址,所以就可以同呼吸共患難了!


參考文獻:
Block技巧與底層解析 by tripleCC

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市九杂,隨后出現的幾起案子珊泳,更是在濱河造成了極大的恐慌跟束,老刑警劉巖瘾敢,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谬擦,死亡現場離奇詭異讼育,居然都是意外死亡苍碟,警方通過查閱死者的電腦和手機牺蹄,發(fā)現死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門忘伞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沙兰,你說我怎么就攤上這事氓奈。” “怎么了僧凰?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵探颈,是天一觀的道長。 經常有香客問我训措,道長伪节,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任绩鸣,我火速辦了婚禮怀大,結果婚禮上,老公的妹妹穿的比我還像新娘呀闻。我一直安慰自己化借,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布捡多。 她就那樣靜靜地躺著蓖康,像睡著了一般。 火紅的嫁衣襯著肌膚如雪垒手。 梳的紋絲不亂的頭發(fā)上蒜焊,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音科贬,去河邊找鬼泳梆。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的优妙。 我是一名探鬼主播乘综,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼套硼!你這毒婦竟也來了卡辰?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤邪意,失蹤者是張志新(化名)和其女友劉穎看政,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體抄罕,經...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年于颖,在試婚紗的時候發(fā)現自己被綠了呆贿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡森渐,死狀恐怖做入,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情同衣,我是刑警寧澤竟块,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站耐齐,受9級特大地震影響浪秘,放射性物質發(fā)生泄漏。R本人自食惡果不足惜埠况,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一耸携、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辕翰,春花似錦夺衍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至壁榕,卻和暖如春矛紫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背护桦。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工含衔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓贪染,卻偏偏與公主長得像缓呛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子杭隙,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內容