關(guān)于Block再啰嗦幾句

1.一共有6種類型:

BLOCK_EXPORT?void?*?_NSConcreteStackBlock[32];

// 只有在調(diào)用copy的情況下才會生成MallocBlock,ARC下會默認(rèn)把棧上的block復(fù)制到堆上

BLOCK_EXPORT?void?*?_NSConcreteMallocBlock[32];

BLOCK_EXPORT?void?*?_NSConcreteGlobalBlock[32];

// ?后面三種主要是用GC

BLOCK_EXPORT?void?*?_NSConcreteWeakBlockVariable[32];

BLOCK_EXPORT void * _NSConcreteAutoBlock[32];

BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32];

2.block是oc對象,含有isa的結(jié)構(gòu)體

3.block只有在主動調(diào)用copy的時候, 才可能會對外部變量(主要講oc對象)的引用計數(shù)器造成影響,會對變量進(jìn)行retain操作,(__block修飾后的變量在MRC下不會對oc對象進(jìn)行retain操作,需要手動管理內(nèi)存),其他所有情況都不會對其引用計數(shù)器造成改變。

但是這里需要明確的是,block結(jié)構(gòu)體內(nèi)部其實會const一份外部變量(以參數(shù)的形式傳入結(jié)構(gòu)體)作為其結(jié)構(gòu)體的成員變量,也就是將變量的副本存到了block結(jié)構(gòu)體,所以對原對象的引用計數(shù)器沒有影響。

然后在block觸發(fā)函數(shù)指針調(diào)用的時候可以直接進(jìn)行值傳遞(注意這里不是賦值的意思,是指函數(shù)方法內(nèi)會生成一個局部變量,也就是外部對象,對它進(jìn)行一個值傳遞,賦值為結(jié)構(gòu)體內(nèi)的變量值,也叫做截取捕捉變量),但是修改內(nèi)容以及復(fù)制都不被允許,因為const化了。(全局與靜態(tài),以及__block修飾 除外)。

其實道理上也可以理解,外部的變量的作用域與生命周期問題,即使block通過指針拿到該變量的修改復(fù)制權(quán)限,其實也很危險,但變量作用域到頭釋放的時候,block內(nèi)部的指針就是野指針。

但是%p打地址的時候你會發(fā)現(xiàn)對象地址還是同一個(因為本質(zhì)上還是同一個對象,外部release了,Block內(nèi)部也用不了)。

4.__block修飾變量相當(dāng)于在block結(jié)構(gòu)體內(nèi)做了一次指針引用。

針對oc對象,__block其實可以看作一種提醒編譯器優(yōu)化的標(biāo)識,加上的話該oc對象會在block結(jié)構(gòu)體內(nèi)會生成一個__block的對象(含有isa的結(jié)構(gòu)體,含有該對象,flag = 131 = BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT,,意思是使用結(jié)構(gòu)體內(nèi)部的copy/dispose輔助函數(shù)手動管理,內(nèi)部無需再次retain,直接指針賦值即可),并且可以使該__block對象在棧和堆上都可以得到(fowarding指針),所以也就是,即使block從棧復(fù)制到堆上,所以加上__block修飾的外部變量,當(dāng)copy到堆上的時候,外部變量不會被復(fù)制(或retain),而是直接賦值指針地址,因此該變量的引用計數(shù)器保持不變。

如果不加的話(flag = BLOCK_FIELD_IS_OBJECT ),當(dāng)copy到堆上時,block會調(diào)用了[obj retain]方法,引用計數(shù)器會+1, 否則復(fù)制到堆上時,會對找不到該變量(因為局部變量受作用域影響,肯定比block的生命周期短),且在復(fù)制的過程中,通過fowarding指針可以找到堆上的該變量。

非oc對象復(fù)制的話,加__block的話,flag = BLOCK_FIELD_IS_BYREF,即使復(fù)制多次也只會復(fù)制一次,后面只是將該變量的引用計數(shù)器+1(但其實對于基本數(shù)據(jù)類型的話,我們也不會去關(guān)心引用計數(shù)器)。不加__block的話,相當(dāng)于block結(jié)構(gòu)體對基本數(shù)據(jù)類型對象做了一次const化到了結(jié)構(gòu)體內(nèi),只能使用,不能修改和賦值。

5.block作為參數(shù),或者作為返回值時,MRC下,一定要copy到堆上,否則會超出作用域會被釋放,導(dǎo)致野指針。這里也是日常代碼時常見場合,建議最好使用ARC,編譯器會默認(rèn)把block復(fù)制到堆上,也就是[[block copy] autorelease]。

6.block對于以參數(shù)形式傳進(jìn)來的對象,不會強(qiáng)引用。因為傳進(jìn)來的參數(shù),根本不會進(jìn)入block的結(jié)構(gòu)體,只是以形參的方式在block觸發(fā)函數(shù)指針時出現(xiàn)。

7.block的循環(huán)引用問題,主要發(fā)生在當(dāng)把block作用成員變量時,切記,如果block內(nèi)部使用了self,或者其他成員變量(基本數(shù)據(jù)類型也不行),都會引起循環(huán)引用,__weak(ARC)和__block(MRC)都可以解決該問題。

e.g.:

__weak _-typeof(self) weakSelf = self;

^{[weakSelf someMethod];}

對block的理解不深的情況下使用,很容易造成循環(huán)引用的泛濫。其實在蘋果原生的設(shè)計系統(tǒng)中,在View與ViewController間的通訊主要還是使用delegate。

而使用block的場合主要是類方法,比如UIView:

[UIView animateWithDuration:1.0 animations:^{

? ? ? ? // 動畫效果

}];

8.全局變量,全部靜態(tài)變量,靜態(tài)變量,不需要加入__block也可以在block中被修改,全局的好理解, 作用域、生命周期都比block要大要長,所以在block內(nèi)可以直接使用、修改。

靜態(tài)變量(存儲在.data區(qū))也是同理,但是方案上,block在結(jié)構(gòu)體內(nèi)部獲取了靜態(tài)變量的指針,所以可以直接修改。

9.^{printf("Hello World!")} 在MRC下,clang后會發(fā)現(xiàn),沒有引入外部變量時,但是block結(jié)構(gòu)體isa指針仍然為StackBlock,但開啟ARC后,isa指針我GlobalStack指針。

推薦幾篇好文&源碼:(源碼難看,但是還得看,否則都是道聽途說)

1.http://m.itdecent.cn/p/e03292674e60#

2.http://m.itdecent.cn/p/51d04b7639f1

3.http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/BlocksRuntime/runtime.c

4.http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/BlocksRuntime/Block_private.h

5.http://blog.ibireme.com/2013/11/27/objc-block/

6.http://mobile.51cto.com/hot-403914.htm

7.http://mobile.51cto.com/hot-403931.htm

8.http://mobile.51cto.com/hot-403935.htm

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容