深刻了解 ios Block 的內部結構

首先複習一下以前寫過的關於 block 的內容網絡

1>>> block 的定義及格式閉包

就拿無返回值 有參數舉個例子算了函數

typedef void(^MyBlock)(NSString * str)翻譯

2>>> block 的幾種類型(三種)代理

_NSConcreteGlobalBlock:全局的靜態 block,相似函數。若是block裏不獲取任何外部變量。或者的變量是全局做用域時,如成員變量、屬性等; 這個時候就是Global類型指針

_NSConcreteStackBlock:保存在棧中的 block,棧都是由系統管理內存,當函數返回時會被銷燬。__block類型的變量也一樣被銷燬。爲了避免被銷燬,block會將block和__block變量從棧拷貝到堆。對象

_NSConcreteMallocBlock:保存在堆中的 block,堆內存能夠由開發人員來控制。當引用計數爲 0 時會被銷燬。blog

因此聲明一個 block屬性時記得用 copy 來修飾內存

3>>> block 的做用作用域

其實就是匿名函數, 閉包, 或者 js那種回調函數, 能夠代替代理模式那種複雜的步驟, 一個最經常使用的用法, 好比網絡請求獲取內容, 先定義一個 block 在你請求完成以後, 調用 block, 那麼當你調用請求方法的時候, 就能夠在 block 裏作解析收到網絡請求內容的操做了, 就像 callback, 或者用來反向傳值也都是比較方便的, 或者用來捕獲當前做用域的變量

4>>> block 須要注意的問題

當在 block 中用到局部變量時, 全局變量能夠修改, 局部變量不能修改他的值, 若是必定要修改, 請用__  block 修飾, 問起緣由, 後面會作詳細解析, __block 修飾的 block 變量其實就是一個結構體

另外一個問題就是循環引用, 例如當前類持有一個 block 的屬性, 而後在 block 的實現裏又引用了當前類, 就會致使循環引用, 當前對象不能釋放 ,內存泄漏, 解決辦法就 weak strong dance, 在 block 外部先用 weak 修飾 在 block 內部再用 strong 修飾

----------------------------------******************-----------------------------------

新建一個 testBlock.m 文件 , 運行 clang -rewrite-objc testBlock.m 能夠翻譯成. cpp的 C++源碼

// 聲明一個 blockName的 block  輸出打印 block 函數 而後執行這個 block

翻譯後的

翻譯成源碼後有不少文件 主要的文件就這四個

 

__main_block_func_0    //這是block要執行的函數, 也就是 block 塊內的內容

 

_main_block_desc_0 // 這是block的描述信息的結構體 對於咱們來講沒什麼做用

 

_main_block_impl_0 // 這就是 block 實現部分的入口 這裏能夠看出一些賦值 和 block 的類型

 

_block_impl  這個就是主角了 這是 block 的真正結構-結構體  含有 isa 指針, 這也是爲何不少人說 block 能夠看作對象的緣由 , 所以 block 也能夠賦值爲 nil

FuncPtr 能夠在 int main 中看到 blockName -> FuncPtr 指向了函數的實現

這個是沒有在 block 內部獲取局部變量的  下面看看 block 內部是如何獲取局部變量的

--------------------------------------**************--------------------------------

咱們在上面的 block 外面聲明一個 int num = 10 的局部變量  而後看他的 cpp 實現 發現有什麼不一樣, 多了一個 num變量, 這裏的__ cself 就至關於 OC 的 self, 在 block 定義的時候就已經把這個 num的變量值拷貝過來了 , 即至關於拷貝了 num 的值進 block 內部, 至關於一個快照, 與外部的 num 變量沒有關係了, 因此無法修改 num 的值

 

再看看__block 修飾變量的時候

這就發生了巨大的變化 多了一個__ Block_byref_num_0 的變量, 這個是什麼呢? 上面就能夠看到這個一個結構體, 裏面有個 num的外部變量, 還有 forwarding的指向堆上的指針

也就是說 加上__ block int num 以後變量就變成了 __Block_byref_num_0的指針, 也便是 num經過這個指針傳遞給了 block, 而不是 block 只捕獲了他的值, 因此block 內部改變變量的值就變成了 在block要執行的函數 __main_block_func_0中,咱們經過__Block_byref_num_0的__forwarding指針來修改的 外部變量,即:(num->__forwarding->num) = 10;

就是修改 forwarding 指向的內存的值 

 

//再囉嗦一點, block 不copy 的話, 是聲明在棧上的 像 int a = 10同樣, copy 以後 block 在堆上 , 同時__ block 修飾的 block 變量也會 copy 一份指針變量到堆上, 而沒有使用__ block 修飾的依然在棧上

相關文章
相關標籤/搜索