iOS OC語言: Block底層實現(xiàn)原理

先來簡單介紹一下Block

Block是什么?

蘋果推薦的類型,效率高,在運行中保存代碼。用來封裝和保存代碼,有點像函數(shù),Block可以在任何時候執(zhí)行。

Block和函數(shù)的相似性:(1)可以保存代碼(2)有返回值(3)有形參(4)調(diào)用方式一樣。

Block 底層實現(xiàn)

定義一個簡單的block

我們再給a賦值為20,此時打印出來a 的值還是10

但當我們在第一次給a 賦值時,前面加上__block 的時候,則打印出來20。

那么為什么加上__block 后 就打印出20了呢,這個原理是什么呢?

其實可以用兩個詞來概括:傳值 和傳址。 可能這樣說大家覺得有點扯,接下來 用C++ 代碼進行編譯。

打開終端做如下操作 在當前文件夾下會得到一個.cpp 文件。點擊鏈接加入群【ios開發(fā)學習】:https://jq.qq.com/

此時打開當前的.cpp 文件(會有差不多10萬行代碼),前面我們都忽略,只需要滾動到最后,此時你會發(fā)現(xiàn)block跟OC中的變化。

接下來我們一個個來看這個block,先來看等號左邊的。

void(*block)()

這是一個沒有參數(shù)沒有返回值的函數(shù)指針,既然是一個函數(shù)指針,那它就是一個變量,變量里面只能保存函數(shù)地址,然后它又在等號的左邊是不是意味著右邊返回的是一個函數(shù)地址(自己推斷)。

再看等號右邊:

((void?(*)())&__main_block_impl_0((void?*)__main_block_func_0,?&__main_block_desc_0_DATA,?a));

參數(shù)(自我推斷):

((void (*)()) 強轉(zhuǎn)(自己理解其實沒有實際含義,不影響自己本身的類型)

& 取址 后面都是函數(shù)的調(diào)用,如果不是也不會得到一個函數(shù)指針的。

__main_block_impl_0 這是一個函數(shù)名,這個函數(shù)有三個參數(shù), com+F 搜索一下,又會發(fā)現(xiàn)這是一個結(jié)構(gòu)體,結(jié)構(gòu)體如下:

struct?__main_block_impl_0?{?struct?__block_impl?impl;?struct?__main_block_desc_0*?Desc;?int?a;

可能你會疑惑,剛剛說這是一個函數(shù),而現(xiàn)在是一個結(jié)構(gòu)體。其實在 c++ 里面結(jié)構(gòu)體相當于OC的類,c++ 里面結(jié)構(gòu)體擁有自己的屬性以及構(gòu)造方法和方法。那么為什么取一個結(jié)構(gòu)體的地址呢? 其實它取得是下面這段代碼的地址:

__main_block_impl_0(void?*fp,?struct?__main_block_desc_0?*desc,?int?_a,?int?flags=0)?:?a(_a)?{?impl.isa?=?&_NSConcreteStackBlock;?impl.Flags?=?flags;?impl.FuncPtr?=?fp;?Desc?=?desc;?}

那么在上面?zhèn)€方法實現(xiàn)里,又有四個參數(shù)。而在剛剛調(diào)用的時候只有三個參數(shù),多了一個參數(shù) flags= 0,這個參數(shù)其實就相當于Swift中指定了一個默認值,不傳也有值,可以忽略。那么后面繼續(xù):

a(_a) : 在 c++ 里面 指定_a(形參) 將來賦值給a 這個實參,也就是這個__main_block_impl_0 結(jié)

構(gòu)體中的 int a;在這里 int a = 10;

impl.FuncPtr = fp; 將fp賦值給了 impl 結(jié)構(gòu)體的 FuncPtr 參數(shù), 在這個參數(shù)里面存放的是下面這段代碼的地址:

static?void?__main_block_func_0(struct?__main_block_impl_0?*__cself)?{?int?a?=?__cself->a;?//?這里?int?a?=?10;?printf("%d\\\\n",a);?//?打印出a?}?__main_block_desc_0_DATA?com+?F?搜索?定義的就是與大小相關(guān)的信息,代碼如下:?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)};

a 直接放a 其實就相當于把a 當前的值拿過來,如果是&a, 就是a的地址。請看下圖:

接下來,又重新給 a賦值為 20,但是Block 最終要找到 FuncPtr 里面存放的是值來執(zhí)行, 在這里才會最終執(zhí)行打印a 的值的代碼,但是這段代碼里 a 是 10 了。所以最終打印的還是10。

最后可以概括為block 底層實現(xiàn) 分兩種:剛剛上面的就是第一種(不加__block), 會創(chuàng)建一個結(jié)構(gòu)體,實現(xiàn)構(gòu)造方法,來接收三個參數(shù)。

接下來看加上__block 的實現(xiàn)。

修改我們的代碼:

再次在終端里面進行編譯,你會發(fā)現(xiàn)生成的結(jié)構(gòu)體會變化。

等號左邊會封裝一個__Block_byref_a_0 結(jié)構(gòu)體類型的變量a,下面是結(jié)構(gòu)體的聲明:

truct?__Block_byref_a_0?{?void?*__isa;?//isa?類型的指針?自己的類型?__Block_byref_a_0?*__forwarding;?//與自己結(jié)構(gòu)體同名,是一個自己類型的結(jié)構(gòu)體的指針,存放的是自己的地址?int?__flags;?//?標記?int?__size;?//?類型的大小?int?a;?//?a?屬性?保存變量的值?};

等號右邊:

{(void*)0,(__Block_byref_a_0?*)&a,?0,?sizeof(__Block_byref_a_0),?10};

參數(shù):

(void*)0?:?一個指針直接存到isa里面?(__Block_byref_a_0?*)&a:?強轉(zhuǎn)?存放的是自己的地址?0?:?會傳給?flags?sizeof(__Block_byref_a_0),?10:?類型的大小?10:?a?的值,?僅僅是創(chuàng)建。

這里僅僅是創(chuàng)建,因為使用了__block 所以創(chuàng)建了一個block 類型的結(jié)構(gòu)體,接下來會才是調(diào)用block,你會發(fā)現(xiàn)其余參數(shù)和第一種實現(xiàn)都一樣,唯一不同的是再去取值的時候,拿到的是結(jié)構(gòu)體的地址,只要把地址傳遞過去,就有了最高的操作權(quán)限,到時候再去取值就可以取到內(nèi)存中最新的值。

接下來(a.__forwarding->a) = 20; 這句代碼是拿到結(jié)構(gòu)體里面的地址去修改a的值為20。

后面再去打印,打印的就是內(nèi)存地址中最新的值,所以就是20。

想學習的小伙伴們可以加裙一起交流哦!626433463

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 先來簡單介紹一下Block Block是什么? 蘋果推薦的類型,效率高,在運行中保存代碼。用來封裝和保存代碼,有點...
    ios軟件開發(fā)學習閱讀 565評論 0 5
  • 前言 Blocks是C語言的擴充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,869評論 0 23
  • 摘要block是2010年WWDC蘋果為Objective-C提供的一個新特性,它為我們開發(fā)提供了便利,比如GCD...
    西門吹雪123閱讀 1,001評論 0 4
  • Blocks Blocks Blocks 是帶有局部變量的匿名函數(shù) 截取自動變量值 int main(){ ...
    南京小伙閱讀 1,077評論 1 3
  • 靜謐,掩飾著我的心跳。 困意,沉淀了我的糾結(jié)。 車窗內(nèi)假寐,只是想試探她的溫柔? 可是怎么會,讓曖昩無處躲躲藏藏。...
    淺畫仲夏閱讀 371評論 3 1

友情鏈接更多精彩內(nèi)容