iOS - 揭露Block的內(nèi)部實(shí)現(xiàn)原理

想必大家對(duì)block都很熟悉了畅涂,雖然都會(huì)用盈包,但是你真的知道它的原理嗎办龄?比如為什么要加上__block废酷,這個(gè)修飾符到底有什么用?不加會(huì)有什么后果慎式?block又是如何實(shí)現(xiàn)的等等伶氢。。瘪吏。該篇文章就為大家揭曉關(guān)于Block的實(shí)現(xiàn)原理~

拋磚引玉

先給出問題癣防,大家思考下結(jié)果吧,如果分別調(diào)用以下兩個(gè)方法肪虎,結(jié)果如何劣砍?

void blockFunc1()
{
    int num = 100;
    void (^block)() = ^{
        NSLog(@"num equal %d", num);
    };
    num = 200;
    block();
}
void blockFunc2()
{
    __block int num = 100;
    void (^block)() = ^{
        NSLog(@"num equal %d", num);
    };
    num = 200;
    block();
}

答案是

blockFunc1 : num equal 100
blockFunc2 : num equal 200

是不是有人答錯(cuò)了?再來兩個(gè)函數(shù)扇救。這兩個(gè)的結(jié)果與blockFunc2一樣刑枝,打印出來的 num 為 200

// 全局變量
int num = 100;
void blockFunc3()
{
    void (^block)() = ^{
        NSLog(@"num equal %d", num);
    };
    num = 200;
    block();
}
void blockFunc4()
{
    static int num = 100;
    void (^block)() = ^{
        NSLog(@"num equal %d", num);
    };
    num = 200;
    block();
}

疑問:
我們發(fā)現(xiàn)num做為局部變量時(shí)加上 _ _block 修飾符、num做為全局變量以及num為靜態(tài)局部變量時(shí)在block中輸出結(jié)果是一樣的迅腔,皆為被修改之后的值装畅,而做為局部變量并且未加上__block的num在block中輸出的值卻還是未賦值之前的值。這是為什么呢沧烈?探索這個(gè)問題我們就需要看看底層結(jié)構(gòu)是如何實(shí)現(xiàn)的了

探索內(nèi)部原理

Objective-C是一個(gè)全動(dòng)態(tài)語言掠兄,它的一切都是基于runtime實(shí)現(xiàn)的!在運(yùn)行時(shí)會(huì)將OC轉(zhuǎn)換成C,我們可以利用這個(gè)來查看關(guān)于block在內(nèi)部是如何實(shí)現(xiàn)的
新建一個(gè)Command Line Tool項(xiàng)目蚂夕,將以上代碼放入main.m中迅诬,如圖

main.m

這里我們打開終端,cd到項(xiàng)目目錄下婿牍,然后將用下面的命令將OC重寫為C

clang -rewrite-objc main.m
rewrite-objc

這時(shí)我們可以發(fā)現(xiàn)當(dāng)前目錄下多了一個(gè)main.cpp文件侈贷,打開它并滾到最下面


打開main.cpp
main.cpp

這里我們可以看到blockFunc1的C語言實(shí)現(xiàn)方法

void blockFunc1()
{
    int num = 100;
    void (*block)() = ((void (*)())&__blockFunc1_block_impl_0((void *)__blockFunc1_block_func_0, &__blockFunc1_block_desc_0_DATA, num));
    num = 200;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

去掉類型轉(zhuǎn)換

void blockFunc1()
{
    int num = 100;
    // *************************重點(diǎn)句***********************
    void (*block)() = &__blockFunc1_block_impl_0(__blockFunc1_block_func_0, &__blockFunc1_block_desc_0_DATA, num));
    // *****************************************************
    num = 200;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

這里我們可以看到

block實(shí)際上是指向結(jié)構(gòu)體的指針

該結(jié)構(gòu)體為


__blockFunc1_block_impl_0

我們來看下帶__block的blockFunc2

blockFunc2

在 blockFunc1 中,block指向了一個(gè)名為__blockFunc1_block_impl_0的結(jié)構(gòu)體等脂,并且在初始化時(shí)輸入了三個(gè)參數(shù)(__blockFunc1_block_impl_0最后的flags有默認(rèn)參數(shù)俏蛮,所以可以不用傳參),第三個(gè)參數(shù)就是我們寫的num上遥,與blockFunc2相比較搏屑,這里的num并沒有帶*號(hào),所以說在這里它只是傳值而非傳址粉楚,而下面的【num = 200;】也就沒什么卵用了辣恋。這就是blockFunc2、blockFunc3與blockFunc4為什么能打印出num改變后的值解幼,而blockFunc1不行的原因抑党。

在這里我們也可以看出:

編譯器會(huì)將block的內(nèi)部代碼生成對(duì)應(yīng)的函數(shù)

** SO **

我們總結(jié)下包警,block在內(nèi)部會(huì)作為一個(gè)指向結(jié)構(gòu)體的指針撵摆,當(dāng)調(diào)用block的時(shí)候其實(shí)就是根據(jù)block對(duì)應(yīng)的指針找到相應(yīng)的函數(shù),進(jìn)而進(jìn)行調(diào)用害晦,并傳入自身

__block的實(shí)現(xiàn)

我們?cè)賮砜纯?_ block特铝, _block也被轉(zhuǎn)換成了結(jié)構(gòu)體,并含有5個(gè)變量

struct __Block_byref_num_0 {
  void *__isa;  // isa指針
__Block_byref_num_0 *__forwarding;  // 實(shí)例本身
 int __flags; 
 int __size;
 int num;  // 我們的num值
};

圖片對(duì)應(yīng)著blockFunc2中的

__block int num = 100;

當(dāng)創(chuàng)建num并用__block修飾的時(shí)候壹瘟,會(huì)初始化這五個(gè)變量
當(dāng)我們執(zhí)行

num = 200;

對(duì)應(yīng)著

(num.__forwarding->num) = 200;

上面剛剛提到過 _ _forwarding是實(shí)例本身鲫剿,即類型結(jié)構(gòu)體__Block_byref_num_0的&num,再找到對(duì)應(yīng)的num變量稻轨,將其原來的100修改為200~~

到此灵莲,關(guān)于Block內(nèi)部實(shí)現(xiàn)的揭曉也就到此結(jié)束了,希望本文能讓你對(duì)block有更深的理解殴俱,感謝你耐心的閱讀政冻!

歡迎關(guān)注微信公眾號(hào):linxunfengtop
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市线欲,隨后出現(xiàn)的幾起案子明场,更是在濱河造成了極大的恐慌,老刑警劉巖李丰,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苦锨,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)舟舒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門拉庶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秃励,你說我怎么就攤上這事砍的。” “怎么了莺治?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵廓鞠,是天一觀的道長。 經(jīng)常有香客問我谣旁,道長床佳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任榄审,我火速辦了婚禮砌们,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘搁进。我一直安慰自己浪感,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布饼问。 她就那樣靜靜地躺著影兽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪莱革。 梳的紋絲不亂的頭發(fā)上峻堰,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音盅视,去河邊找鬼捐名。 笑死,一個(gè)胖子當(dāng)著我的面吹牛闹击,可吹牛的內(nèi)容都是我干的镶蹋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼赏半,長吁一口氣:“原來是場噩夢啊……” “哼贺归!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起除破,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤牧氮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瑰枫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踱葛,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丹莲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尸诽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甥材。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖性含,靈堂內(nèi)的尸體忽然破棺而出洲赵,到底是詐尸還是另有隱情,我是刑警寧澤商蕴,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布叠萍,位于F島的核電站,受9級(jí)特大地震影響绪商,放射性物質(zhì)發(fā)生泄漏苛谷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一格郁、第九天 我趴在偏房一處隱蔽的房頂上張望腹殿。 院中可真熱鬧,春花似錦例书、人聲如沸锣尉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽自沧。三九已至,卻和暖如春织狐,著一層夾襖步出監(jiān)牢的瞬間暂幼,已是汗流浹背筏勒。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工移迫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人管行。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓厨埋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捐顷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荡陷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

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

  • 《Objective-C高級(jí)編程》這本書就講了三個(gè)東西:自動(dòng)引用計(jì)數(shù)、block迅涮、GCD废赞,偏向于從原理上對(duì)這些內(nèi)容...
    WeiHing閱讀 9,798評(píng)論 10 69
  • Block基礎(chǔ)回顧 1.什么是Block? 帶有局部變量的匿名函數(shù)(名字不重要叮姑,知道怎么用就行)唉地,差不多就與C語言...
    Bugfix閱讀 6,748評(píng)論 5 61
  • 序言:翻閱資料据悔,學(xué)習(xí),探究耘沼,總結(jié)极颓,借鑒,謝謝探路者群嗤,我只是個(gè)搬運(yùn)工菠隆。參考、轉(zhuǎn)發(fā)資料:http://www.cnbl...
    Init_ZSJ閱讀 899評(píng)論 0 1
  • 摘要 Blocks是C語言的擴(kuò)充功能狂秘, iOS 4中引入了這個(gè)新功能“Blocks”骇径,那么block到底是什么東西...
    CholMay閱讀 1,156評(píng)論 2 10
  • iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對(duì)C語言的擴(kuò)展,用來實(shí)現(xiàn)匿名函數(shù)的特性,B...
    smile刺客閱讀 2,331評(píng)論 2 26