http://blog.csdn.net/jasonblog/article/details/7756763
這篇文章寫得不錯理郑,我梳理一下悲敷。馁龟。怨酝。
首先block用Apple文檔的話來說傀缩,“A block is an anonymous inline collection of code, and sometimes also called a "closure".
一個block是一個匿名內聯(lián)代碼的集合,有時候也叫”closure“凫碌。
但這幾句話寫得比較籠統(tǒng)扑毡,block到底是什么呢?
block提供了一種新的方式進行回調盛险,并且用block進行回調還可以直接訪問局部變量瞄摊,這是一般的函數做不到的勋又。
這份文檔中提到block的幾種適用場合:任務完成時回調處理,消息監(jiān)聽回調處理换帜,錯誤回調處理楔壤,枚舉回調,視圖動畫惯驼、變換蹲嚣,排序。
那么實際上block到底在由底層表現(xiàn)為什么呢祟牲?
其實也是一個結構體(下圖)隙畜,所謂的__main_block_impl_0就是block的具體實現(xiàn),這是最簡單的block的實現(xiàn)说贝,既不需要操作block外的變量议惰。
block結構體包含了__block_impl
由此,一個block就是一個包含了一系列信息的結構體乡恕,包含函數指針言询,類對象的指針,描述信息等等傲宜。
那么稍微復雜一點的block呢运杭?比如操作了block外的變量的block是什么樣的呢?
看到圖中的i了嗎函卒,這就是block外部的變量辆憔,只不過這是不能修改只能訪問。
那為什么不能修改只能訪問呢?因為i傳進來只是進行了值傳遞谆趾,所以block所包含的函數和main函數的作用域是不同的躁愿,你要是修改了,就無法保證內部和外部的數據一致性沪蓬。我們當然可以把i從int換成某個指針變量來實現(xiàn)局部變量的修改。
不過block常常用作回調来候,所以假如block還沒執(zhí)行到跷叉,但是外部函數已經從棧彈出,那這樣再用指針訪問不僅沒有意義营搅,更有可能造成非法訪問的錯誤云挟。
所以在這兒不允許block做修改局部變量。
那么转质,到底怎樣才能修改局部變量呢园欣?
1.全局變量、靜態(tài)全局變量是可以在block中直接進行修改的休蟹,就不存在上面說的可能產生的問題沸枯。
2.在局部變量前面加上__block 指示符日矫。
第1個很好理解,第2個是什么意思呢绑榴?
下圖是一個__block變量對應的結構體
由第一個成員__isa指針也可以知道__Block_byref_i_0也可以是NSObject哪轿。
第二個成員__forwarding指向一個__Block_byref_i_0結構,這個很重要翔怎。
最后一個成員是目標存儲變量i窃诉。
而此時block變成下面這樣
可以看到,i-__forwarding初始化是指向了自己赤套,為什么要指向自己飘痛?指向自己是沒有意義的,只能說有時候需要指向另一個__block結構容握。
對應的函數__main_block_func_0(block所指向的函數)如下:

亮點是__Block_byref_i_0指針類型變量i敦冬,通過其成員變量__forwarding指針來操作另一個成員變量。 為什么要這么做呢唯沮?待會兒再說脖旱。
先解答為什么可以修改局部變量的問題:
通過這樣看起來有點復雜的改變,我們可以修改變量i的值介蛉。但是問題同樣存在:__Block_byref_i_0類型變量i仍然處于棧上萌庆,當block被回調執(zhí)行時,變量i所在的棧已經被展開币旧,怎么辦践险?
在這種關鍵時刻,__main_block_desc_0站出來了:

此時吹菱,__main_block_desc_0多了兩個成員函數:copy和dispose巍虫,分別指向__main_block_copy_0和__main_block_dispose_0。
當block從棧上被copy到堆上時鳍刷,會調用__main_block_copy_0將__block類型的成員變量i從棧上復制到堆上占遥;而當block被釋放時,相應地會調用__main_block_dispose_0來釋放__block類型的成員變量i输瓜。
那么現(xiàn)在來解決為什么要通過__forwarding來操作局部變量瓦胎。
因為變量一會在棧上,一會在堆上尤揣,那如果棧上和堆上同時對該變量進行操作搔啊,怎么辦?
這時候北戏,__forwarding的作用就體現(xiàn)出來了:當一個__block變量從棧上被復制到堆上時负芋,棧上的那個__Block_byref_i_0結構體中的__forwarding指針也會指向堆上的結構。
所以實際上修改的都是堆上的__Block_byref_i_0結構體嗜愈,main函數釋放的時候旧蛾,只是釋放了棧上的東西莽龟。而所有的對局部變量的修改都早已經轉移到堆上了。