Block 是將函數(shù)及其執(zhí)行上下文封裝起來(lái)的對(duì)象(結(jié)構(gòu)體)。Block的調(diào)用實(shí)質(zhì)上就是函數(shù)的調(diào)用。
----它的內(nèi)存是分配在棧上的,當(dāng)賦值的成員變量調(diào)用block時(shí),可能會(huì)因?yàn)闂I蠈?duì)應(yīng)的函數(shù)退出后在內(nèi)存中被釋放,而導(dǎo)致內(nèi)存崩潰。為了防止這個(gè)問(wèn)題,所以都是把block拷貝到堆中使用。
(在ARC下,使用copy和strong是一樣的,因?yàn)閎lock的retain是用copy來(lái)實(shí)現(xiàn)的,不過(guò)一般習(xí)慣性使用copy)
一、截獲變量
二、__block修飾符
三、避免block循環(huán)引用
一. block對(duì)不同變量的截獲
1>.對(duì)于基本數(shù)據(jù)類(lèi)型的局部變量只截獲其值。
2>.對(duì)于對(duì)象類(lèi)型的局部變量連同所有權(quán)修飾符一起截獲(強(qiáng)引用)。
3>.以指針形式截獲局部靜態(tài)變量。
4>.不截獲全局變量、靜態(tài)全局變量,直接進(jìn)行使用。
1.普通局部變量
int age = 10;
void(^localBlock)(void) = ^{
NSLog(@"age = %d", age);
};
age = 18;
localBlock();
//輸出age = 10;在age改變?yōu)?8之前,localBlock已經(jīng)截獲age的值(10)保存到localBlock結(jié)構(gòu)體中
二. __block修飾的變量
== 對(duì)于用 __block 修飾的變量(實(shí)際變成了一個(gè)結(jié)構(gòu)體),在截獲時(shí),block結(jié)構(gòu)體中保存了被修飾變量的地址和值,相當(dāng)于進(jìn)行指針拷貝。在block中可以修改這樣的變量的值。
__block int age = 10;
void(^localBlock)(void) = ^{
NSLog(@"age = %d", ++age);
};
age = 18;
localBlock();
/*
輸出age = 19;另外解釋下當(dāng)去除__block修飾時(shí),會(huì)報(bào)錯(cuò)not assignable,
原因是局部變量保存在棧中,而block保存在堆中,不在一塊內(nèi)存空間里,而你block只知道我的值不知道我的家在哪里,當(dāng)然不能修改我。
*/
一般情況,對(duì)截獲的對(duì)象類(lèi)型局部變量進(jìn)行賦值操作需添加__block修飾符。賦值不等于使用(賦值是初始化,例如向已存在的數(shù)組中添加數(shù)據(jù)就不屬于賦值而只是使用,就不必使用__block修飾符)。
__block NSMutableArray *mArr = [[NSMutableArray alloc] init];
void(^localBlock)(void) = ^{
//使用mArr 不需要 __block修飾
[mArr addObject:@"1212"];
//給mArr賦值 需要 __block修飾
mArr = [[NSMutableArray alloc] initWithObjects:@"222", nil];
};
localBlock();
三. 解決block的循環(huán)引用(讓你從此告別只會(huì)weakSelf的青蔥歲月)
1.weak->strong
self.name = @"James";
__weak typeof(self) weakSelf = self;
void(^localBlock)(void) = ^(){
__strong typeof(self) strongself = weakSelf;
//此處延遲兩秒執(zhí)行,模擬數(shù)據(jù)請(qǐng)求或者耗時(shí)操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
NSLog(@"%@",strongself.name);
});
};
localBlock();
strongSelf使用解釋?zhuān)?/strong>
----試想一種情況,當(dāng)block在執(zhí)行耗時(shí)操作時(shí),當(dāng)前控制器被推出,而此時(shí)weakSelf由于在弱引用表中會(huì)馬上被釋放,block對(duì)self的弱引用沒(méi)有引用計(jì)數(shù)加持,就導(dǎo)致block作用域內(nèi)的self也會(huì)立刻置為nil。
----而如果在block的作用域中設(shè)置一個(gè)臨時(shí)變量對(duì)weakSelf進(jìn)行強(qiáng)引用,使block擁有對(duì)self的引用計(jì)數(shù),這樣即使在控制器被推出后,也因?yàn)閎lock強(qiáng)持有了控制器而不會(huì)馬上被釋放。
---而strongSelf由于是block作用域內(nèi)的局部變量,也會(huì)在block執(zhí)行完成后被自動(dòng)釋放池解決掉,此時(shí)控制器最終釋放,這樣既能避免循環(huán)引用也能夠保證block代碼塊的完整執(zhí)行。
2.__block修飾
self.name = @"James";
__block CurrentVC *vc = self;
void(^localBlock)(void) = ^(){
//此處延遲兩秒執(zhí)行,模擬數(shù)據(jù)請(qǐng)求或者耗時(shí)操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
//__block修飾的對(duì)象會(huì)被block強(qiáng)引用,需要手動(dòng)解環(huán)
/*
此處還要解釋的一點(diǎn)是,雖然上文說(shuō)__block修飾相當(dāng)于做了指針拷貝,
但實(shí)際情況 vc是將原self的地址和內(nèi)容作為兩個(gè)引用保存在一個(gè)結(jié)構(gòu)體內(nèi),
所以vc置nil并不影響外部的self
*/
vc = nil;
});
};
localBlock();
- self作為參數(shù)
void(^localBlock)(CurrentVC *vc) = ^(CurrentVC *vc){
NSLog(@"%@",vc.name);
};
localBlock(self);
如果有疏漏的地方,也希望各位coder不吝指教