一议薪、Block定義
?????Block是什么?Block是一個帶自動變量的匿名函數(shù)媳友,大家知道C語言中沒有匿名函數(shù)斯议,所以可以說Block是C語言的的一種拓展。從OC的角度來看醇锚,Block是一個OC 對象哼御,它內(nèi)部也有isa指針坯临。
那么Block的實質(zhì)是什么?這是一段簡單的Block定義:
int main(int argc,char* argv[]) {
? ? @autoreleasepool {
? ? ? ? int a=10;
? ? ? ? void(^BlockName)() = ^() {
? ? ? ? ? ? NSLog(@"block內(nèi)部a-%d",a);
? ? ? ? };
? ? ? ? BlockName();
? ? }
}
然后用命令行轉(zhuǎn)化成C++ 看其內(nèi)部實現(xiàn)
clang -rewrite-objc main.m
我們可以看到Block中調(diào)用了__main_block_impl_0函數(shù)恋昼,并將這個函數(shù)的地址賦值給了Block,我們看下__main_block_impl_0這個結(jié)構(gòu)體的具體實現(xiàn):
我們可以看到這個結(jié)構(gòu)體內(nèi)部還有一個同名構(gòu)造函數(shù)__main_block_impl_0的看靠,構(gòu)造函數(shù)傳遞了 四個參數(shù):
第一個參數(shù)__main_block_func_0 : 取出對應(yīng)a參數(shù) copy,NSLog...液肌,這個函數(shù)對應(yīng)存儲了原來Block中的代碼挟炬;
第二個參數(shù)__main_block_desc_0_DATA: 其內(nèi)包含兩個參數(shù)?reserved、Block_size矩屁,并且reserved賦值為0而Block_size則存儲著__main_block_impl_0的占用空間大小辟宗,傳遞對應(yīng)的地址入?yún)ⅲ?/p>
第三個參數(shù)a:就是我們定義的局部變量。
所以吝秕,這邊也可以理解了泊脐,當(dāng)局部變量傳入Block之后,再次修改局部變量的值之后是無法被Block捕獲的烁峭。因為局部變量參數(shù)是作為值傳入Block內(nèi)部的存儲在__main_block_impl_0結(jié)構(gòu)體內(nèi)部的容客,因此修改局部變量值之后Block輸出還是之前存儲的那個值。
可以通過下圖看下Block內(nèi)各個函數(shù)之間的關(guān)系:
__Block修飾后可以修改局部變量???
我們來看下__Block修飾后的c++實現(xiàn):
我們可以看到原來?__block int a=10;會被處理成?__Block_byref_a_0 a=...再看下這個結(jié)構(gòu)體實現(xiàn):
可以看到結(jié)構(gòu)體內(nèi)有一個forwarding指針和一個與原變量相同類型的成員變量约郁,forwarding指針指向結(jié)構(gòu)體內(nèi)的成員變量缩挑。在__Block修飾的變量,會被實現(xiàn)為__Block_byref_a_0類型鬓梅,在block內(nèi)外供置,都通過forwarding來訪問的。這是與之前的值類型傳遞不一樣的地方绽快,也是能修改局部變量的原因芥丧。
二、Block使用場景
這是Block最完整的定義:
/* returnType:返回類型
? ??blockName:函數(shù)名稱
????parameterTypes:參數(shù)類型
????parameters: 參數(shù)名稱
? ? */
<#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
? ? ? ? ? ? <#statements#>
? ? ? ? };
Block因其方便易讀的特點在我們?nèi)粘i_發(fā)中使用較為頻繁:
1坊罢、typedef Blcok屬性续担,通過Block實現(xiàn)事件的響應(yīng)或者數(shù)據(jù)的傳遞
typedef? void(^Block) (int a,int b);
@property (nonatomic,copy) Block *myBlock;
2、Block作為方法參數(shù)活孩,利用Block實現(xiàn)回調(diào)
? ? [UIView animateWithDuration:<#(NSTimeInterval)#> animations:<#^(void)animations#> completion:<#^(BOOL finished)completion#>]
3物遇、利用Block返回自身self對象等實現(xiàn)鏈?zhǔn)秸Z法
MASonry使用
三、Block內(nèi)存管理
Block主要有三種類型
__NSGlobalBlock__ ( _NSConcreteGlobalBlock )
?__NSStackBlock__ ( _NSConcreteStackBlock )
?__NSMallocBlock__ ( _NSConcreteMallocBlock )
看下不同類型的Block的內(nèi)存分配
那么這幾種類型的Block都是如何定義的呢
__NSGlobalBlock__:沒有訪問auto對象憾儒,存放在數(shù)據(jù)段中
__NSStackBlock__:訪問了auto對象询兴,存放在棧中;
__NSMallocBlock__:__NSStackBlock__調(diào)用copy成為__NSMallocBlock__類型起趾,并保存在堆中蕉朵,由程序員負(fù)責(zé)管理。而這些很多情況下在都由ARC幫我們處理阳掐。
四始衅、Block循環(huán)引用
在Block內(nèi)使用self冷蚂,一般會導(dǎo)致循環(huán)引用,這是為什么呢汛闸?
循環(huán)引用是什么意思呢蝙茶?簡單來說就是堆上的對象與堆上的對象互相引用造成的環(huán)Block作為self的屬性,self對Block是強引用诸老,而在Block內(nèi)部引用self某個屬性隆夯,實現(xiàn)了Block對self的強引用,循環(huán)引用就這樣產(chǎn)生了别伏。
為了避免循環(huán)引用蹄衷,同時為了防止異步的block在回調(diào)的時,block執(zhí)行的過程中被意外釋放厘肮,我們可以先定義一個weak類型的對象供Block內(nèi)部使用愧口,再用__strong 對block外的__weak對象再次強引用,既能夠防止引用循環(huán)类茂,又能夠保證代碼的正確執(zhí)行耍属。
? ? __weaktypeof(self) weakSelf =self;
? ? _myBlock= ^(inta ,intb){
? ? ? ? __strongtypeof(self) strongSelf = weakSelf;
? ? ? ? strongSelf.age =10;
? ? };
參考
iOS底層原理總結(jié)