天天看點

iOS Block捕獲外部變量和ARC自動拷貝block

運作結果

分析

方法内部的obj變量在棧中,變量記憶體位址0x7fff543d0c98,所指向的對象<nsobject: 0x7fcb1bd22390>在堆中,記憶體位址0x7fcb1bd22390,它的引用計數+1。

<code>block中obj指針已經不是外部的obj指針了,它是外部變量obj的拷貝</code>,它的記憶體位址是0x7fcb1c903fb0,跟外部obj不一樣,但是所指向的對象也是0x7fcb1bd22390,0x7fcb1bd22390對象引用計數再+1=2。

block中的obj指針(記憶體位址是0x7fcb1c903fb0)對對象(&lt;nsobject: 0x7fcb1bd22390&gt;)的引用是強引用,在外部将obj(位址0x7fff543d0c98)置為nil後,外部的obj不再指向&lt;nsobject: 0x7fcb1bd22390&gt;對象,0x7fcb1bd22390對象的引用計數-1,引用計數為1,<code>0x7fcb1bd22390對象記憶體不被自動回收</code>,是以第二次調用block,0x7fcb1bd22390對象還在記憶體中。

結論

block内部的obj 指針是外部obj指針的拷貝,有2個指針指向同一個nsobject對象,但隻将外部的obj指針置為nil,nsobject對象的引用計數不為0,無法回收。

分析 :

方法内部的weakobj變量在棧中,變量記憶體位址0x7fff543d0c98,所指向的對象&lt;nsobject: 0x7fcb1bd2fee0&gt;在堆中,記憶體位址0x7fcb1bd2fee0,它的引用計數+1。

weakobj變量也在棧中,記憶體為0x7fff543d0c90,所指向的對象也是&lt;nsobject: 0x7fcb1bd2fee0&gt;,弱引用,是以0x7fcb1bd2fee0對象的引用計數不增加,仍然為1.

block中weakobj它的記憶體位址是0x7fcb1bc072b0,跟外部的weakobj不同,但是所指向的對象也是0x7fcb1bd2fee0,弱引用,0x7fcb1bd2fee0對象引用計數還是不增加,仍然是1。

-在外部将obj(位址0x7fff543d0c98)置為nil後,外部的obj不再指向&lt;nsobject: 0x7fcb1bd2fee0&gt;對象,0x7fcb1bd2fee0對象的引用計數-1,引用計數為0,arc回收0x7fcb1bd2fee0對象記憶體,并将指向它的弱引用指針指派為nil,是以第二次調用block,0x7fcb1bd2fee0對象不在在記憶體中。

在block中__weak聲明的指針去引用對象 可以避免循環引用的問題,但是當外部對象被釋放了,block 内部會通路不到這個對象. 這種問題如何解決呢?先來看一段代碼:

執行結果如下:

總結 :

多線程的時候,在 <code>block 外部</code>用<code>__weak</code>聲明的變量指向一個對象, 通過把weak聲明的變量值指派給<code>block内部</code>的```strong<code>變量</code>,實作在block内對該對象進行強引用,這樣可以在block生命周期内保留該對象不被釋放,在block生命周期結束後,對象記憶體被釋放。

第3處日志列印了一個testblock對象,blockobj的位址發生變化。此時,<code>block對象</code> 從棧拷貝到堆上,__block變量blockobj,也被拷貝到堆上。block對象<code>擁有</code>blockobj指針指向的對象。注意:這是個強引用哦。

關注4到8 處日志,用__block關鍵字聲明blockobj指針後,block内外的變量blockobj都是0x7fa084905838,也就是block内外的blockobj指針是同一個指針。

block内部改變 blockobj指針指向的對象,改動在 block外部可見。

<a href="https://www.zybuluo.com/microcai/note/51116" target="_blank">block實作原理(一)</a>

<a href="https://www.zybuluo.com/microcai/note/57603" target="_blank">block和變量的記憶體管理(二)</a>

在oc,在block中直接通路外部變量,通路的是外部變量的copy。用clang後将 .m翻譯為.cpp檔案後發現,外部函數是通過<code>傳值方式</code>将變量值傳給block(block結構體、block最終要執行的函數代碼).

使用了__block後,外部函數是通過<code>指針傳遞</code>,将變量傳遞到 block 内,是以可以修改變量值.

block作為c語言的擴充,并不是高新技術,和其他語言的閉包或lambda表達式是一回事。需要注意的是由于objective-c在ios中不支援gc機制,使用block必須自己管理記憶體,而記憶體管理正是使用block坑最多的地方,錯誤的記憶體管理 要麼導緻return cycle記憶體洩漏要麼記憶體被提前釋放導緻crash。 block的使用很像函數指針,不過與函數最大的不同是:block可以通路函數以外、詞法作用域以内的外部變量的值。換句話說,block不僅 實作函數的功能,還能攜帶函數的執行環境。

可以這樣了解,block其實包含兩個部分内容:

block執行的代碼,這是在編譯的時候已經生成好的;

一個包含block執行時需要的所有外部變量值的資料結構。 block将使用到的、作用域附近的變量建立一份快照拷貝

根據block在記憶體中的位置分為三種類型:_nsconcreteglobalblock,_nsconcretemallocblock, _nsconcretestackblock。

<code>_nsconcreteglobalblock</code>:類似函數,位于text段;

<code>_nsconcretestackblock</code>:位于棧記憶體,函數傳回後block将無效;

<code>_nsconcretemallocblock</code>:位于堆記憶體。

blocks that don't capture any variables are global blocks. since all instances of the block are the same, the compiler can just allocate one copy statically for the life of the program。

blocks that capture variables (closures) are either stack or heap (malloc) blocks. blocks start out on the stack, as stack blocks. when a stack block is copied for the first time, it is moved to the heap. copying a heap block does not create another copy; but simply retains it.

在開啟 arc 時,大部分情況下編譯器通常會将建立在棧上的 block 自動拷貝到堆上。

當 block 作為函數傳回值傳回時,編譯器自動将 block 作為 _block_copy 函數,效果等同于 block 直接調用 copy 方法;

當 block 被指派給 __strong id 類型的對象或 block 的成員變量時,編譯器自動将 block 作為 _block_copy 函數,效果等同于 block 直接調用 copy 方法;

當 block 作為參數被傳入方法名帶有 usingblock 的 cocoa framework 方法或 gcd 的 api 時。這些方法會在内部對傳遞進來的 block 調用 copy 或 _block_copy 拷貝;

在沒有arc之前,由于arc 自動将棧中block拷貝到堆上,是以當returnblock函數退出,在棧中記憶體釋放後,仍然可以通路到block對象。

在以下情形中, block 會從棧拷貝到堆:

當 block 調用 copy 方法時,如果 block 在棧上,會被拷貝到堆上;

當 block 作為參數被傳入方法名帶有 usingblock 的 cocoa framework 方法或 gcd 的 api 時。這些方法會在内部對傳遞進來的 block 調用 copy 或 _block_copy 進行拷貝;

其他情況需要手動拷貝。

程式會報exc_bad_access ,getblockarray傳回的數組裡面的 block 是不可通路的。

手動copy後,block拷貝到堆上,getblockarray函數傳回的棧幀被銷毀後,仍可以通路堆中的block拷貝。

推薦文章:

<a href="http://www.tanhao.me/pieces/310.html/" target="_blank">http://www.tanhao.me/pieces/310.html/</a>

<a href="http://www.tanhao.me/pieces/310.html/" target="_blank">block 記憶體管理</a>

<a href="http://devtian.me/2015/05/10/ios-block-1/" target="_blank">block 實作原理</a>

<a href="http://clang.llvm.org/docs/block-abi-apple.html" target="_blank">block-abi-apple</a>

<a href="http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/" target="_blank">正确使用block避免cycle retain和crash</a>

<a href="http://honglu.me/2015/01/06/weak%e4%b8%8eblock%e5%8c%ba%e5%88%ab/" target="_blank">weak與block差別</a>

<a href="http://cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html" target="_blank">how blocks are implemented (and the consequences</a>

<a href="http://clang.llvm.org/docs/block-abi-apple.html" target="_blank">block實作原理</a>