循環(huán)引用
對象持有Block, Block 又捕獲該對象
__weak不會產(chǎn)生強引用,指向的對象銷毀時,會自動將指針置為nil。因此一般通過__weak來解決問題。
__unsafe_unretained不會產(chǎn)生強引用,不安全,指向的對象銷毀時,指針存儲的地址值不變。
使用__block也可以解決循環(huán)引用的問題, 只是在block執(zhí)行之后才會把__block的結構體置為nil,解除循環(huán)引用
捕獲對象
Block 會捕獲局部變量,不會捕獲全局變量,因為全局變量在哪里都可以訪問,不需要捕獲。
注意:為什么會捕獲self
因為無論是對象方法或類方法,都會默認將self作為參數(shù)傳遞到方法內(nèi)部,作為參數(shù)的話就是局部變量,局部變量就會被Block捕獲。
訪問方式:
- auto變量: 值傳遞 (值捕獲)
- 局部static變量:指針傳遞 (地址捕獲)
- 全局變量:直接訪問 (不捕獲)
內(nèi)存區(qū)域變化
新創(chuàng)建的Block默認在數(shù)據(jù)段中,引用局部變量時會捕獲變量,且Block會變?yōu)闂^(qū);
棧區(qū)Block被copy或賦值給Strong變量后會變?yōu)槎褏^(qū)Block;
堆區(qū)Block被copy后類型不發(fā)生改變,引用計數(shù)增加
ARC環(huán)境下,編譯器會根據(jù)情況自動將棧區(qū)的Block進行一次copy操作,將Block復制到堆上, 以下情況會進行copy操作:
- Block作為函數(shù)返回值時
- 將Block賦值給 __strong指針時
- Block作為Cocoa API中方法含有usingBlock的方法參數(shù)時
- Block作為GCD API的方法參數(shù)時
對象類型的引用
- Block捕獲的變量為對象類型,會生成copy和dispose方法對內(nèi)部引用的對象進行內(nèi)存管理
- Block在棧上,訪問對象類型auto變量時,不會對改變量產(chǎn)生強引用,不論Block結構體內(nèi)部的變量時__strong修飾還是__weak修飾
- 如果Block被拷貝到堆上,copy函數(shù)會調(diào)用assign函數(shù),根據(jù)auto變量的修飾符去形成強引用或弱引用
- 如果Block從堆中移除,會調(diào)用dispose函數(shù)自動釋放引用的auto變量
Block內(nèi)部不能修改局部變量的原因
- Block內(nèi)部是值引用,拿到的只是Block內(nèi)部的值,因此無法修改外部的變量
- 使用static修飾基本數(shù)據(jù)類型變量可以拿到變量地址,因此可以在Block內(nèi)部修改外部變量值
- __block修飾可以解決Block內(nèi)部不能修改變量值的問題,不可以用來修飾static變量和全局變量。__block將變量包裝成對象,把變量封裝在結構體中,Block內(nèi)部存儲的是結構體指針,可以通過指針找到內(nèi)存地址進而修改變量的值
- 僅僅使用內(nèi)存地址,而不是修改該變量的話,不用添加__block,否則系統(tǒng)會自動創(chuàng)建相應的結構體進行存儲,占用不必要的內(nèi)存