一.什么是block
Block是將函數(shù)及其執(zhí)行上下文封裝起來的對象。
如下:
通過 命令編譯該.m?文件,發(fā)現(xiàn)該?block?被編譯成這個形式:
__block_impl?結(jié)構(gòu)體為
block內(nèi)部有?isa?指針击奶,所以說其本質(zhì)也是?OC?對象 block內(nèi)部則為:
所以說Block是將函數(shù)及其執(zhí)行上下文封裝起來的對象既然Block內(nèi)部封裝了函數(shù),那么它同樣也有參數(shù)和返回值责掏。
二柜砾、Block?變量截獲
1、局部變量截獲 是值截獲换衬。 比如:
這里的輸出是 6 而不是 2痰驱,原因就是對局部變量?num?的截獲是值截獲证芭。 同樣,在?block?里如果修改變量?num担映,也是無效的废士,甚至編譯器會報錯。
打印為 1蝇完,2官硝,3局部對象變量也是一樣,截獲的是值短蜕,而不是指針氢架,在外部將其置為 nil,對?block?沒有影響忿危,而該對象調(diào) 用方法會影響.
2达箍、局部靜態(tài)變量截獲 是指針截獲。
輸出為 2铺厨,意味著 num = 1 這里的修改 num 值是有效的缎玫,即是指針截獲。 同樣解滓,在 block 里去修改變量 m赃磨,也是有效的。
3洼裤、全局變量邻辉,靜態(tài)全局變量截獲:不截獲,直接取值。
我們同樣用?clang?編譯看下結(jié)果腮鞍。
?static NSInteger num3 = 300;
??NSInteger num4 = 3000;
編譯后
(?impl.isa?=?&_NSConcreteStackBlock;這里注意到這一句值骇,即說明該?block?是棧?block) 可以看到局部變量被編譯成值形式,而靜態(tài)變量被編成指針形式移国,全局變量并未截獲吱瘩。而__block 修飾的變 量也是以指針形式截獲的,并且生成了一個新的結(jié)構(gòu)體對象:
該對象有個屬性:num5迹缀,即我們用 __block修飾的變量使碾。這里__forwarding?是指向自身的(棧 block)。一般情況下祝懂,如果我們要對 block 截獲的局部變量進行賦值操作需添加__block修飾符票摇,而對全局變量,靜態(tài)變量是不需要添加__block修飾符的砚蓬。 另外矢门,block?里訪問?self?或成員變量都會去截獲。
三、Block?的幾種形式
分為全局Block(_NSConcreteGlobalBlock)颅和、棧Block(_NSConcreteStackBlock)傅事、堆Block(_NSConcreteMallocBlock)三種形式其中棧 Block 存儲在棧(stack)區(qū),堆?Block?存儲在堆(heap)區(qū)峡扩,全局?Block?存儲在已初始化數(shù)據(jù)(.data)區(qū)
1、不使用外部變量的?block?是全局?block
比如:
輸出:
2障本、使用外部變量并且未進行?copy?操作的?block?是棧?block
比如:
輸出:
3教届、對棧?block?進行?copy?操作,就是堆?block驾霜,而對全局?block?進行?copy案训,仍是全局?block
比如堆1中的全局進行copy操作,即賦值:
輸出:
而對2中的棧block進行賦值操作:
輸出:
對棧?blockcopy?之后粪糙,并不代表著棧?block?就消失了强霎,左邊的?mallock?是堆?block,右邊被?copy?的仍是棧?block蓉冈。比如:
輸出:
即如果對棧 進行copy城舞,將會copy到堆區(qū),對堆Block進行copy寞酿,將會增加引用計數(shù)家夺,對全局Block?進行 copy,因為是已經(jīng)初始化的伐弹,所以什么也不做拉馋。
另外,__block 變量在 時惨好,由于__forwarding?的存在煌茴,棧上的 指針會指向堆上的 g 變量,而堆上的 指針指向其自身日川,所以蔓腐,如果對 的修改,實際上是在修改堆上的 __block 變量逗鸣。
即__forwarding?指針存在的意義就是合住,無論在任何內(nèi)存位置,都可以順利地訪問同一個__block?變量撒璧。另外由于block捕獲的 修飾的變量會去持有變量透葛,那么如果用__block修飾self,且self持有Block卿樱,并且 block 內(nèi)部使用到__block修飾的self時僚害,就會造成多循環(huán)引用,即?self?持有?block,block 持有__block變量 萨蚕,__block變量持有self靶草,造成內(nèi)存泄漏。
如果要解決這種循環(huán)引用岳遥,可以主動斷開__block變量對self的持有奕翔,即block內(nèi)部使用完weakself后將其置為 nil,但這種方式有個問題浩蓉,如果 一直不被調(diào)用派继,那么循環(huán)引用將一直存在。 所以捻艳,我們最好還是用__weak?來修飾.