iOS OC语言: Block底层实现原理
來源http://www.wtoutiao.com/p/11dgbk4.html
先來簡單介紹一下Block
Block是什么?
蘋果推薦的類型,效率高,在運行中保存代碼。用來封裝和保存代碼,有點像函數,Block可以在任何時候執行。
Block和函數的相似性:(1)可以保存代碼(2)有返回值(3)有形參(4)調用方式一樣。
Block 底層實現
定義一個簡單的block
我們再給a賦值為20,此時打印出來a 的值還是10
但當我們在第一次給a 賦值時,前面加上__block 的時候,則打印出來20。
那么為什么加上__block 后 就打印出20了呢,這個原理是什么呢?
其實可以用兩個詞來概括:傳值 和傳址。 可能這樣說大家覺得有點扯,接下來 用C++ 代碼進行編譯。
打開終端做如下操作 在當前文件夾下會得到一個.cpp 文件。
此時打開當前的.cpp 文件(會有差不多10萬行代碼),前面我們都忽略,只需要滾動到最后,此時你會發現block跟OC中的變化。
接下來我們一個個來看這個block,先來看等號左邊的。
void(*block)()這是一個沒有參數沒有返回值的函數指針,既然是一個函數指針,那它就是一個變量,變量里面只能保存函數地址,然后它又在等號的左邊是不是意味著右邊返回的是一個函數地址(自己推斷)。
再看等號右邊:
((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));-
參數(自我推斷):
((void (*)()) 強轉(自己理解其實沒有實際含義,不影響自己本身的類型)
& 取址 后面都是函數的調用,如果不是也不會得到一個函數指針的。
__main_block_impl_0 這是一個函數名,這個函數有三個參數, com+F 搜索一下,又會發現這是一個結構體,結構體如下:
struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;int a;可能你會疑惑,剛剛說這是一個函數,而現在是一個結構體。其實在 c++ 里面結構體相當于OC的類,c++ 里面結構體擁有自己的屬性以及構造方法和方法。那么為什么取一個結構體的地址呢? 其實它取得是下面這段代碼的地址:
__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;}那么在上面個方法實現里,又有四個參數。而在剛剛調用的時候只有三個參數,多了一個參數 flags= 0,這個參數其實就相當于Swift中指定了一個默認值,不傳也有值,可以忽略。那么后面繼續:
a(_a) : 在 c++ 里面 指定_a(形參) 將來賦值給a 這個實參,也就是這個__main_block_impl_0 結構體中的 int a;在這里 int a = 10;
impl.FuncPtr = fp; 將fp賦值給了 impl 結構體的 FuncPtr 參數, 在這個參數里面存放的是下面這段代碼的地址:
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 搜索 定義的就是與大小相關的信息,代碼如下: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 里面存放的是值來執行, 在這里才會最終執行打印a 的值的代碼,但是這段代碼里 a 是 10 了。所以最終打印的還是10。
最后可以概括為block 底層實現 分兩種:剛剛上面的就是第一種(不加__block), 會創建一個結構體,實現構造方法,來接收三個參數。
接下來看加上__block 的實現。
修改我們的代碼:
再次在終端里面進行編譯,你會發現生成的結構體會變化。
等號左邊會封裝一個__Block_byref_a_0 結構體類型的變量a,下面是結構體的聲明:
truct __Block_byref_a_0 {void *__isa; //isa 類型的指針 自己的類型__Block_byref_a_0 *__forwarding; //與自己結構體同名,是一個自己類型的結構體的指針,存放的是自己的地址int __flags; // 標記int __size; // 類型的大小int a; // a 屬性 保存變量的值};等號右邊:
{(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};-
參數:
(void*)0 : 一個指針直接存到isa里面(__Block_byref_a_0 *)&a: 強轉 存放的是自己的地址0 : 會傳給 flagssizeof(__Block_byref_a_0), 10: 類型的大小10: a 的值, 僅僅是創建。
這 里僅僅是創建,因為使用了__block 所以創建了一個block 類型的結構體,接下來會才是調用block,你會發現其余參數和第一種實現都一樣,唯一不同的是再去取值的時候,拿到的是結構體的地址,只要把地址傳遞過 去,就有了最高的操作權限,到時候再去取值就可以取到內存中最新的值。
接下來(a.__forwarding->a) = 20; 這句代碼是拿到結構體里面的地址去修改a的值為20。
后面再去打印,打印的就是內存地址中最新的值,所以就是20。
作者:Liwjing 地址:http://www.jianshu.com/users/8df89a9d8380/latest_articles轉載于:https://www.cnblogs.com/sundaysgarden/p/5602456.html
總結
以上是生活随笔為你收集整理的iOS OC语言: Block底层实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32精英版(正点原子STM32F1
- 下一篇: WIN7的便签使用快捷键