Objective-C的Block實(shí)質(zhì)與實(shí)現(xiàn)探究 part-1

Block僅用作輸出語句的情況

</br>

int main() {
    void (^blk)(void) = ^{ printf("Block\n"); };
    blk();
    
    return 0;
}

轉(zhuǎn)換為C++后:

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

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("Block\n");
}

static struct __main_block_desc_0 {
    unsigned long reserved;
    unsigned long Block_size;
}  __main_block_desc_0_DATA = {
      0,
      sizeof(struct __main_block_impl_0);
};

int main() {

    void (*blk)(void) = (void(*)(void)) &__main_block_impl_0( (void *)__main_block_func_0, &__main_block_desc_0_DATA);
    
    ((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr) ((struct __block_impl *)blk);


    return 0;
}

轉(zhuǎn)換后代碼量激增,不要急著驚詫。

main()函數(shù)前面的一大堆代碼只不過是結(jié)構(gòu)體的定義而已傲隶,我們看到最最主要的main()函數(shù)并沒有發(fā)生多大的改變喊括,轉(zhuǎn)換前后都是3條語句而已胧瓜,所以先從main()函數(shù)切入。

</br>

1.1 main函數(shù)第一條語句分析

</br>
為了便于觀察分析郑什,先把“長且臭”的類型轉(zhuǎn)換去掉:

void (^blk)(void) = ^{ printf("Block\n"); };
//vs
void (*blk)(void) = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA);

?可以發(fā)現(xiàn)府喳,代碼塊不過是一個(gè)函數(shù)的指針而已。那右邊賦值給它的必然就是一個(gè)函數(shù)的地址了:

&__main_block_impl_0(para1, para2);

沒錯(cuò)吧蘑拯?這個(gè)函數(shù)是結(jié)構(gòu)體__main_block_impl_0的構(gòu)造函數(shù)劫拢。

根據(jù)名字,我們還可以推斷出這個(gè)結(jié)構(gòu)體的意義:這個(gè)結(jié)構(gòu)體是為了實(shí)現(xiàn)在main()函數(shù)中定義的?Block强胰,在此例是blk舱沧。末尾追加數(shù)字0,用以標(biāo)識不同的Block偶洋。

既然說到構(gòu)造函數(shù)了熟吏,那自然要看看這個(gè)結(jié)構(gòu)體的成員變量,里面都有什么:

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
};
Block數(shù)據(jù)結(jié)構(gòu)圖(1)

impl變量展開后:


Block數(shù)據(jù)結(jié)構(gòu)圖(2)

Block里面含有void *isa成員變量玄窝,可見Block是對象牵寺,而且一般來說它是分配在棧中的對象。

void *isa是什么恩脂?可瀏覽筆者另文:《Objective-C類的實(shí)質(zhì)是結(jié)構(gòu)體》下文也會有粗略說明帽氓。

下面看構(gòu)造函數(shù)是怎么給這些成員變量賦值的:

__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;
};

void (*blk)(void) = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA);
Block數(shù)據(jù)結(jié)構(gòu)圖(3)
  • isa指針,用于說明該blk實(shí)例(因其也是對象俩块,可以說成是Block實(shí)例)是什么“Block類”黎休,類似于說明
    studentStudent類一樣(Student *student = [Student alloc] init]; )∮窨總共有3種Block類:
    改自《Objective-C高級編程iOS與OS X多線程和內(nèi)存管理 表2-3 Block的類》
    所以上面才會說Block一般是分配在棧中的對象势腮,下面還會繼續(xù)討論。
  • Flags
  • Reserved
  • FuncPtr 一個(gè)函數(shù)指針漫仆,指向Block的實(shí)現(xiàn)代碼捎拯,即^{... }部分,上例中是^{ printf("Block\n") ;}
  • Descdescriptor縮寫盲厌。是指向存放?代碼塊描述符的指針署照。里面有個(gè)Block_size變量,聲明了塊對象的總體大小吗浩。

雖然Block還可能包含其他一些上面未列出的屬性建芙,但上面的屬性是每個(gè)Block都有的屬性。

接著拓萌,觀察FuncPtr指向的Block實(shí)現(xiàn)代碼:

static void __main_block_func_0 (struct __main_block_impl_0 *__cself) {
    printf("Block\n");
}

__cself指針指向塊的結(jié)構(gòu)體__main_block_impl_0變量岁钓,所以__cselfC++中的thisObjective-C中的self類似微王。
</br>

1.2 main函數(shù)第二條語句分析

blk();
//vs
((void (*)(struct __block_impl *))((struct __block_impl *)blk)->FuncPtr) ((struct __block_impl *)blk);

“長且臭”去掉后:

(*blk->impl.FuncPtr)(blk);

前文已述屡限,FuncPtr是一個(gè)函數(shù)指針,指向Block的實(shí)現(xiàn)代碼炕倘,所以(*blk->impl.FuncPtr)()是函數(shù)指針調(diào)用函數(shù)(即Block的實(shí)現(xiàn)代碼)钧大,其中傳入了參數(shù)blkblk作為參數(shù)傳入__cself罩旋。

題外話:blk實(shí)例準(zhǔn)確來說應(yīng)該是一個(gè)struct __main_block_impl_0 *類型啊央,在使用blk時(shí),卻將其強(qiáng)制轉(zhuǎn)換成struct __block_impl *涨醋,使外部代碼只能訪問blkstruct __block_impl *為身份的那些變量瓜饥,從而對塊描述符Desc和構(gòu)造函數(shù)進(jìn)行了封裝。體現(xiàn)了封裝性浴骂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乓土,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子溯警,更是在濱河造成了極大的恐慌趣苏,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梯轻,死亡現(xiàn)場離奇詭異食磕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)喳挑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門彬伦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人伊诵,你說我怎么就攤上這事媚朦。” “怎么了日戈?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵奉狈,是天一觀的道長括饶。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么衫樊? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮匀们,結(jié)果婚禮上献汗,老公的妹妹穿的比我還像新娘。我一直安慰自己资厉,他們只是感情好厅缺,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般湘捎。 火紅的嫁衣襯著肌膚如雪诀豁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天窥妇,我揣著相機(jī)與錄音舷胜,去河邊找鬼。 笑死活翩,一個(gè)胖子當(dāng)著我的面吹牛烹骨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播材泄,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼沮焕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拉宗?” 一聲冷哼從身側(cè)響起峦树,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎簿废,沒想到半個(gè)月后空入,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡族檬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年歪赢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片单料。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡埋凯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扫尖,到底是詐尸還是另有隱情白对,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布换怖,位于F島的核電站甩恼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沉颂。R本人自食惡果不足惜条摸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铸屉。 院中可真熱鬧钉蒲,春花似錦、人聲如沸彻坛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至钙蒙,卻和暖如春茵瀑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仪搔。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工瘾婿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜻牢,地道東北人烤咧。 一個(gè)月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像抢呆,于是被迫代替她去往敵國和親煮嫌。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

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