block的底層結(jié)構(gòu):
- block定義:block的本質(zhì)是一個的OC對象瑰步,內(nèi)部有一個isa指針贵白,它封裝了函數(shù)和調(diào)用函數(shù)所需要的參數(shù)衣吠,block中有一個變量捕獲機制锰什,正是因為這個變量捕獲機制罚随,block才能封裝函數(shù)的調(diào)用環(huán)境玉工。
-
block的底層結(jié)構(gòu)圖:
block的變量捕獲(capture)
- 為了保證在block內(nèi)部能夠訪問外部變量,block有個變量的捕獲機制淘菩。block對局部變量和全局變量的訪問方式是不同的
-
auto:當前變量的作用域為當前函數(shù)或代碼塊內(nèi)遵班,當前變量時一個局部變量屠升,當前變量會在棧區(qū)上進行分配存儲空間。
- 在block中直接使用到成員變量也會捕獲self狭郑。
block的類型
可以通過調(diào)用class方法或者isa指針查看block的具體類型腹暖,最終都是繼承NSBlock類型
block的三種類型:
block的類型 | 環(huán)境 |
---|---|
__NSGlobalBlock__ | 沒有訪問auto變量 |
__NSStackBlock__ | 訪問了auto變量 |
__NSMallocBlock__ | __NSStackBlock__ 調(diào)用了copy之后 |
-
block每種類型在內(nèi)存中存儲的區(qū)域:
-
在ARC環(huán)境下,編譯器會根據(jù)情況自動將棧上的block復制到堆上
- block作為函數(shù)的返回值時
- block賦值給__strong指針時
- block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時
- block作為GCD API的方法參數(shù)時
-
MRC下block屬性修飾關(guān)鍵字一定要使用copy:
- @property (copy, nonatomic) void (^block)(void);
-
ARC下Block屬性修飾關(guān)鍵字建議使用copy(使用strong和使用copy的效果是一樣的):
- @property (strong, nonatomic) void (^block)(void);
- @property (copy, nonatomic) void (^block)(void);
-
每種類型的block調(diào)用了多次copy之后:
-
block內(nèi)部訪問了對象類型的auto變量時:
如果block是在棧上的時候翰萨,不會對auto變量產(chǎn)生強引用
-
如果block被copy到堆上的時候
- 會調(diào)用block內(nèi)部的copy函數(shù)脏答,copy函數(shù)會調(diào)用_Block_object_assign函數(shù)
- _Block_object_assign函數(shù)會根據(jù)auto變量的修飾符決定得是強引用或者弱引用。
-
如果block從堆上移除的時候
- 會調(diào)用block內(nèi)部的dispose函數(shù),dispose函數(shù)會調(diào)用_Block_object_dispose函數(shù)
- _Block_object_dispose函數(shù)會自動對強引用的auto變量做一次release
__block的原理
-
__block的本質(zhì)
- __block可以用來解決在block內(nèi)部無法修改auto變量的問題亩鬼,編譯器會將__block修飾的變量封裝成一個對象殖告。
-
__block只能用來修飾auto變量,不能修飾static 和全局變量
-
捕獲了OC對象或者捕獲了使用__block修飾的臨時變量的block結(jié)構(gòu)體中為什么會有forwaring指針?
-
block對__block變量的內(nèi)存管理
- block對__block變量的內(nèi)存管理
- 當block棧上的時候并不會對__block變量產(chǎn)生強引用雳锋。 當block被copy到堆上的時候會調(diào)用block內(nèi)部的copy函數(shù)黄绩,copy函數(shù)會調(diào)用_Block_object_assign函數(shù),_Block_object_assign函數(shù)會對__block變量強引用魄缚。
- 當block從堆中移除的時候宝与,會調(diào)用block的dispose函數(shù),dispose函數(shù)會調(diào)用_Block_object_dispose函數(shù),__Block_object_dispose函數(shù)會對__block變量做一次release
-
__block變量對修飾的對象類型的內(nèi)存管理
- __block變量在棧上的時候不會對__block內(nèi)部的變量產(chǎn)生強引用
- 當__block變量被copy到堆上的時候 會調(diào)用__block變量內(nèi)部的copy方法冶匹。 copy方法會調(diào)用 __Block_object_assign 函數(shù)习劫,_Block_object_assign函數(shù)會根據(jù)所指向?qū)ο蟮男揎椃麤Q定是強指針還是弱指針(僅僅是ARC環(huán)境下,在MRC環(huán)境下一定不會retain)
-
當__block變量從堆上移除的時候會調(diào)用__block變量內(nèi)部的dospose函數(shù)嚼隘, dispose函數(shù)會調(diào)用_Block_object_dispose诽里,_Block_object_dispose會對指向的對象做一次release
block的循環(huán)引用
block產(chǎn)生循環(huán)引用的原因
-
在block中使用了self或者成員變量
__block變量產(chǎn)生的循環(huán)引用
__block變量持有對象
對象持有block
-
block持有__block
解決block的循環(huán)引用的方法
-
在ARC下的方法:
- __weak、__unsafe_unretained 關(guān)鍵字來解決循環(huán)引用
- 使用__block變量來解決循環(huán)引用(必須要調(diào)用block)
-
在MRC下的方法
- 使用__unsafe_unretained 或者__block來解決循環(huán)引用