Objective-C底層探究之block(一)

iOS SDK 4.0開始映挂,Apple引入了block這一特性。趁最近比較閑,來(lái)研究一下block底層實(shí)現(xiàn)方式卖漫。先來(lái)看一段簡(jiǎn)單的代碼

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 10;
        int b = 11;
        int (^block)(int,int) = ^(int a, int b) {
            return a+b;
        };
        block(a,b);
    }
    return 0;
}

在上面代碼中創(chuàng)建了一個(gè)帶有兩個(gè)參數(shù)的block并調(diào)用它。將代碼保存為 main.m文件使用命令

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

將代碼轉(zhuǎn)換為底層c++赠群。會(huì)生成一個(gè)main.cpp文件羊始。包含大約3w行代碼。直接看最后面關(guān)鍵部分:

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 int __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {

            return a+b;
        }

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)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        int a = 10;
        int b = 11;
        int (*block)(int,int) = ((int (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((int (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, a, b);
    }
    return 0;
}

在main函數(shù)里查描,我們可以看到block變量創(chuàng)建方式突委。聲明block是一個(gè)指向int (*)(int,int)類型的函數(shù)指針。內(nèi)部的值為

((int (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA))

看起來(lái)很頭疼冬三。我們來(lái)一步一步分解匀油。
首先發(fā)現(xiàn)調(diào)用了一個(gè)函數(shù)

__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA))

__main_block_impl_0 是什么東西呢,翻看main函數(shù)上面的代碼我們可以發(fā)現(xiàn) __main_block_impl_0是一個(gè)結(jié)構(gòu)體勾笆,內(nèi)部為

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

也就是說(shuō) 我們的block本質(zhì)上其實(shí)是一個(gè)指向 __main_block_impl_0結(jié)構(gòu)體的指針敌蚜。
該結(jié)構(gòu)體里面有兩個(gè)變量:

  • struct __block_impl impl
  • struct __main_block_desc_0 * Desc

__block_impl__main_block_desc_0 類型 搜索上面代碼可以找到類型定義:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
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)};

可以看到 __block_impl 里面有一個(gè)isa指針。大膽猜測(cè)其實(shí)block本質(zhì)也是一個(gè)Objective-C對(duì)象窝爪。
__main_block_desc_0里面儲(chǔ)存著__main_block_impl_0變量的大小也就是block變量所占的內(nèi)存弛车。

構(gòu)造該結(jié)構(gòu)體時(shí)傳入了兩個(gè)參數(shù)齐媒。第一個(gè)參數(shù)為__main_block_func_0再翻看上面代碼我們發(fā)現(xiàn)** __main_block_func_0**是一個(gè)函數(shù)

static int __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {

            return a+b;
        }

對(duì)比一下,發(fā)現(xiàn)函數(shù)里面實(shí)現(xiàn)跟我們?cè)綽lock內(nèi)部實(shí)現(xiàn)是一樣的纷跛。所以這個(gè)函數(shù)應(yīng)該就是block本體喻括。這個(gè)函數(shù)指針被傳入了** __main_block_impl_0**結(jié)構(gòu)體里面的 impl 中,被 FuncPtr 變量持有贫奠。而第二個(gè)參數(shù)大概是一些描述信息唬血。
到了調(diào)用時(shí)我們的調(diào)用被轉(zhuǎn)換成了:

((int (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, a, b);

又是一連串的類型轉(zhuǎn)換。我們慢慢來(lái)抽:
從括號(hào)里面大致可以看出

((int (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)

是一個(gè)函數(shù)頭唤崭,后面是傳入?yún)?shù)
繼續(xù)分析發(fā)現(xiàn)實(shí)質(zhì)上是調(diào)用了

block->FuncPtr

這個(gè)函數(shù)刁品,而通過(guò)前面我們得知FuncPtr函數(shù)實(shí)質(zhì)上就是我們包裹在block中的代碼
這里有一個(gè)問題,我們知道block是一個(gè)__main_block_impl_0類型的對(duì)象浩姥。而這個(gè)類型里面直接拿不到FuncPtr的挑随。要通過(guò)impl才能拿到的。那為什么這里可以這么寫呢勒叠。
我們知道C語(yǔ)言(包括C++)結(jié)構(gòu)體本質(zhì)是直接按照先后順序(整齊)排列在內(nèi)存中的兜挨。而取結(jié)構(gòu)體內(nèi)部的本質(zhì)就是內(nèi)存偏移。impl變量是__main_block_impl_0類型的第一個(gè)變量眯分。所以impl的初始內(nèi)存地址就是block的初始內(nèi)存地址拌汇。FuncPtrblock中偏移位置與在impl中偏移位置是一樣的。所以可以通過(guò)類型轉(zhuǎn)換

(__block_impl *)block)->FuncPtr

直接獲取FuncPtr這個(gè)函數(shù)調(diào)用弊决。
以上

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末噪舀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子飘诗,更是在濱河造成了極大的恐慌与倡,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昆稿,死亡現(xiàn)場(chǎng)離奇詭異纺座,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)溉潭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門净响,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人喳瓣,你說(shuō)我怎么就攤上這事馋贤。” “怎么了畏陕?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵配乓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)扰付,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任仁讨,我火速辦了婚禮羽莺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘洞豁。我一直安慰自己盐固,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布丈挟。 她就那樣靜靜地躺著刁卜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪曙咽。 梳的紋絲不亂的頭發(fā)上蛔趴,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音例朱,去河邊找鬼孝情。 笑死,一個(gè)胖子當(dāng)著我的面吹牛洒嗤,可吹牛的內(nèi)容都是我干的箫荡。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼渔隶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼羔挡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起间唉,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绞灼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后呈野,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镀赌,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年际跪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了商佛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姆打,死狀恐怖良姆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幔戏,我是刑警寧澤玛追,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響痊剖,放射性物質(zhì)發(fā)生泄漏韩玩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一陆馁、第九天 我趴在偏房一處隱蔽的房頂上張望找颓。 院中可真熱鬧,春花似錦叮贩、人聲如沸击狮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)彪蓬。三九已至,卻和暖如春捺萌,著一層夾襖步出監(jiān)牢的瞬間档冬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工桃纯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捣郊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓慈参,卻偏偏與公主長(zhǎng)得像呛牲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子驮配,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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