對(duì)于經(jīng)常使用的Block愧旦,你不得不知的東東~~~
先上菜,看看最原始的block使用
int i = 0;
int main(int argc, const char * argv[]) {
@autoreleasepool {
//1.block的定義,block就是變量名
int (^block)(int,int);
//2.block的實(shí)現(xiàn), 可以直接將實(shí)現(xiàn)的代碼寫在其他的函數(shù)或者方法中杆怕,函數(shù)指針不可以
block = ^int(int a, int b){
return a + b;
};
//3.block的調(diào)用
int r = block(3,4);
NSLog(@"r =%d",r);
//4.block的使用的注意點(diǎn)族购。
//4.1 block和局部變量
__block int a = 10;
int (^block1)(int) = ^int(int b){//定義和實(shí)現(xiàn)一起寫
//a = 100;block默認(rèn)情況下不可以修改外部的局部變量,若要修改加關(guān)鍵字__block
a = 90;
return a + b;//block內(nèi)部可以直接使用外部的變量
};
int r2 = block1(2);
NSLog(@"%d",r2);
//4.2block和全局變量
int(^block3)(int) = ^int(int a){
i = 9;
return a + i;
};
r = block3(10);
NSLog(@"r = %d",r);
//5.block的其它寫法
//5.1實(shí)現(xiàn)block的時(shí)候返回子=值類型可以不寫
int (^block4)(int) = ^(int a){
return a;
};
r = block4(20);
NSLog(@"r =%d",r);
//5.2沒有參數(shù)的時(shí)候陵珍,實(shí)現(xiàn)block的時(shí)候()可以省略
int (^block5)() = ^{
return 100;
};
r = block5();
NSLog(@"%d",r);
}
return 0;
}
block的實(shí)質(zhì)是什么寝杖?一共有幾種block?都是什么情況下生成的互纯?
閉包是什么瑟幕?閉包是一個(gè)函數(shù)(或指向函數(shù)的指針),再加上該函數(shù)執(zhí)行的外部的上下文變量(自由變量)留潦。Block 就是 Objective-C 對(duì)于閉包的對(duì)象實(shí)現(xiàn)只盹。即,Block 的實(shí)質(zhì)是對(duì)象兔院。為什么說 Block 是一個(gè)對(duì)象呢殖卑,原因就在于 isa 指針。在 Block_layout 的數(shù)據(jù)結(jié)構(gòu)定義里坊萝,有 isa 指針懦鼠。所有的對(duì)象都有 isa 指針,用于實(shí)現(xiàn)對(duì)象相關(guān)功能
有3中類型的block
全局 Block:_NSConcreteGlobalBlock
全局 Block 的結(jié)構(gòu)體實(shí)例設(shè)置在程序的數(shù)據(jù)存儲(chǔ)區(qū)屹堰,所以可以在程序的任意位置通過指針來訪問肛冶。_NSConcreteGlobalBlock 是全局的 Block,在編譯期間就已經(jīng)決定了扯键,如同宏一樣睦袖。
以下 2 個(gè)條件只要滿足 1 個(gè)就可以產(chǎn)生全局 Block:
? 記述全局變量的地方有 Block 語法時(shí)。
? Block 不截獲自動(dòng)變量時(shí)荣刑。
棧 Block:_NSConcreteStackBlock
生成 Block 后馅笙,如果這個(gè) Block 不是全局 Block,那么它就是 _NSConcreteStackBlock 對(duì)象厉亏,但是如果其所屬的變量作用域結(jié)束董习,該 Block 就被廢棄。
如果 Block 變量用 __block 復(fù)制到了堆上爱只,則不會(huì)再收到變量作用域結(jié)束的影響皿淋,因?yàn)樗兂闪硕?Block。
堆 Block:_NSConcreteMallocBlock
將棧 Block 復(fù)制到堆以后恬试,Block 結(jié)構(gòu)體的 isa 成員變量變成了 _NSConcreteMallocBlock窝趣。
大多數(shù)情況下,編譯器會(huì)進(jìn)行判斷训柴,自動(dòng)將 Block 從棧復(fù)制到堆:
? Block 作為函數(shù)值返回的時(shí)候
? 部分情況下哑舒,向方法或者函數(shù)中傳遞 Block 的時(shí)候
o Cocoa 框架的方法且方法名中含有 usingBlock 等時(shí)
o GCD 的 API
除來這 2 種情況,基本都需要我們手動(dòng)復(fù)制 Block幻馁。
為什么在默認(rèn)情況下無法修改被block捕獲的變量洗鸵? __block都做了什么越锈?
? 默認(rèn)情況下,Block 里面的變量膘滨,拷貝進(jìn)去的是變量的值瞪浸,而不是指向變量的內(nèi)存指針。
? 當(dāng)使用 __block 修飾后的變量吏祸,拷貝到 Block 里面的就是指向變量的指針对蒲,就可以修改變量的值。
注意: MRC贡翘,如果沒有用 __block蹈矮,會(huì)對(duì)外部對(duì)象采用 copy 操作,而用了 __block 則不會(huì)采用 copy 操作鸣驱。
- MRC泛鸟,__block 根本不會(huì)對(duì)指針?biāo)赶虻膶?duì)象進(jìn)行 copy 操作,只是把指針進(jìn)行復(fù)制踊东。
- ARC北滥,對(duì)應(yīng)聲明為 __block 的外部對(duì)象,Block 內(nèi)部會(huì)進(jìn)行 retain闸翅,以至于在 Block 環(huán)境內(nèi)能安全引用外部對(duì)象再芋。
解決方式
1。 __block 修飾符坚冀,用于指定將改變變量的存儲(chǔ)區(qū)域(從棧到堆)2济赎。 改變存儲(chǔ)于特殊存儲(chǔ)區(qū)域的變量(全局變量、靜態(tài)全局變量记某、靜態(tài)變量)
block的內(nèi)存管理
? 無論當(dāng)前環(huán)境是ARC還是MRC,只要block沒有訪問外部變量,block始終在全局區(qū)
? MRC情況下
? block如果訪問外部變量,block在棧里
? 不能對(duì)block使用retain,否則不能保存在堆里
? 只有使用copy,才能放到堆里
? ARC情況下
? block如果訪問外部變量,block在堆里
? block可以使用copy和strong,并且block是一個(gè)對(duì)象
? 為什么用copy修飾
? 為什么要用copy修飾司训,這是因?yàn)樵贛RC時(shí)期,作為屬性的block在初始化時(shí)是被存放在靜態(tài)區(qū)的液南,這樣在使用時(shí)如果block內(nèi)有調(diào)用外部變量壳猜,那么block無法保留其內(nèi)存,在初始化的作用域內(nèi)使用并不會(huì)有什么影響滑凉,但一但出了block的初始化作用域统扳,就會(huì)引起崩潰,使用copy可以將block的內(nèi)存推入堆中譬涡,這樣讓其擁有保存調(diào)用的外部變量的內(nèi)存的能力闪幽。
block的注意點(diǎn)
1)在block內(nèi)部使用外部指針且會(huì)造成循環(huán)引用情況下,需要用__weak修飾外部指針
__weak typeof(self) weakSelf = self;
2)在block內(nèi)部如果調(diào)用了延時(shí)函數(shù)還使用弱指針會(huì)取不到該指針,因?yàn)橐呀?jīng)被銷毀了,需要在block內(nèi)部再將弱指針重新強(qiáng)引用一下
__strong typeof(self) strongSelf = weakSelf;
3)如果需要在block內(nèi)部改變外部變量的話,需要在用__block修飾外部變量