Blocks是C語言的擴(kuò)充功能。可以用一句話來表示Blocks的擴(kuò)充功能:帶有自動變量(局部變量)的匿名函數(shù)扭仁。----------------《Objective-C高級編程》
通常致稀,我們命名一個(gè)函數(shù)宗苍,具備函數(shù)名钻弄、參數(shù)名成肘、返回參數(shù)名。
eg:(void*)print:(NSString*)name;
/*
*print:函數(shù)名
name:參數(shù)名
NSString * :參數(shù)類型
(void*)返回值類型斧蜕,這個(gè)其實(shí)是無返回值的
*/
函數(shù)在內(nèi)存中,占有一個(gè)塊內(nèi)存砚偶,稱為代碼塊(或者說是函數(shù)塊)批销。如果此時(shí)有一個(gè)指針指向了這個(gè)函數(shù)地址洒闸,我們就稱這個(gè)指針就是函數(shù)指針。那么這個(gè)函數(shù)指針是什么類型呢均芽?
蓋住函數(shù)名丘逸,就是函數(shù)指針的類型;(void*)(*)(NSString *)
Block的一般寫法為:void(^blk)();
Void為返回類型,blk為Block的名稱掀宋,無參數(shù)傳入
NSString *(^name)(NSString *)=^(NSSting * n){
NSLog(@"name is %@",n);
return n;
}
其中傳入?yún)?shù)深纲、返回值都為NSString。
Block一般分三步走:1劲妙、聲明2湃鹊、實(shí)現(xiàn)3、調(diào)用镣奋。
1币呵、截獲自動變量
typedef void(^blk_t)();
int i = 1;
blk_t ?blk_t2 = ^{
printf("i = %d\n",i);
};
blk_t2();// __NSMallocBlock__
__block int b =2;
blk_t ? blk_t21 = ^{
b =3;
printf("b = %d\n",b);
};
blk_t21();// __NSMallocBlock__
當(dāng)我們用__block 修飾自動變量的時(shí)候,在block的內(nèi)部將 自動變量編譯成了結(jié)構(gòu)
體侨颈,而在block中擁有指向該結(jié)構(gòu)體的指針變量余赢,從而能夠修改自動變量的值
當(dāng)Block從棧中復(fù)制到堆中時(shí),相應(yīng)的__block變量也會復(fù)制到堆中哈垢。此時(shí)妻柒,棧中的
__forwarding指針指向堆中的 __block變量地址。而堆中的
__block變量的__forwarding 指針指向自己耘分。所以我們訪問的 __block變量不會存在問題举塔。
2、從棧復(fù)制到堆(摘自O(shè)bjective-C高級編程)
1.調(diào)用block的copy實(shí)例方法
2.Block作為函數(shù)返回值返回時(shí)
3.將Block賦值附有__strong修飾符id類型 或Block類型成員變量時(shí)
4陶贼、在方法名中含有usingBlock 的Cocoa 框架方法或GCD的API中傳遞Block時(shí)
在調(diào)用Block的copy 實(shí)例方法時(shí)啤贩。如果Block配置在棧上,那么Blcok會從棧復(fù)制到堆拜秧。
Block作為函數(shù)返回值返回時(shí)痹屹、將Block賦值給附有__strong修飾符id類型的類,或者Block類型變量時(shí)枉氮,編譯器自動將對象的Block作為參數(shù)并調(diào)用_Block_copy
函數(shù)志衍,這與調(diào)用Block的copy實(shí)例方法的效果相同。
在方法名中含有usingBlock的Cocoa框架方法或GCD的API傳遞Block時(shí)聊替,在該方法或函數(shù)內(nèi)部堆傳遞過來的Block調(diào)用Block的copy實(shí)例方法 或者 ——Block_copy函數(shù)
通過使用-__ strong 修飾符的自動變量楼肪,Block中截獲的對象就能超出其變量作用域而存在
3、__weak弱引用防止內(nèi)存泄漏
一般滴惹悄,我們在block中調(diào)用self.prop或者self時(shí)春叫,用__weak來修飾指針變量
Id _weak tmp = self;
Blk =^{NSLog(@"self = %@",tmp)};
這樣就能解決循環(huán)引用的問題
4、使用__block修飾 防止內(nèi)存泄露
#import"MyObject.h"
typedef void(^blk_t)(void);
@interface MyObject()
{
blk_t blk_;
}
@end
@implementationMyObject
- (instancetype)init
{
self= [super init];
if(self) {
__block id tmp =self;
blk_= ^{
NSLog(@"self =%@",tmp);
tmp= nil;
};
}
return self;
}
- (void)execBlcok{
blk_();
}
@end
intmain(intargc,const char* argv[]) {
@autoreleasepool{
id obj = [[MyObject alloc]init];
[obj execBlcok];
}
}
該源碼沒有引起循環(huán)引用。但是不調(diào)用execBlock方法暂殖,即不能執(zhí)行賦值給成員變量blk_的Block价匠,就會循環(huán)引用,并引起內(nèi)存泄漏呛每。
1踩窖、MyObject持有Block
2、Block持有__block變量
3晨横、__block變量持有MyObject對象
調(diào)用execBlock方法洋腮,Block實(shí)例被執(zhí)行,nil會被復(fù)制到__block變量的tmp中
此時(shí)手形,__block變量tmp對MyObject類對象的強(qiáng)引用失效啥供。避免了循環(huán)引用
1、MyObject持有Block
2叁幢、Block持有__block變量
使用__block變量的優(yōu)點(diǎn):
1滤灯、通過__block變量可控制對象的持有
期間
2、在不能使用__weak修飾符的環(huán)境中不使用_unsafe_unretained
修飾符即可(不用擔(dān)心懸垂指針)
在執(zhí)行Block時(shí)可動態(tài)地決定是否將nil或者其他對象賦值在__block變量中
缺點(diǎn):
為了避免循環(huán)引用必須執(zhí)行Block
Block的鏈?zhǔn)骄幊?/b>
Block的鏈?zhǔn)骄幊棠軌蜉^少代碼量曼玩,讓代碼更加簡潔鳞骤。
#import
@interfaceChainObject :NSObject
- (ChainObject*(^)(NSString*))name;
- (ChainObject*(^)(NSString*))write;
@end
#import"ChainObject.h"
@implementationChainObject
- (ChainObject*(^)(NSString*))name{
return^(NSString* n){
NSLog(@"他是%@",n);
return self;
};
}
- (ChainObject*(^)(NSString*))write{
return^(NSString* w){
NSLog(@"他能寫%@",w);
return self;
};
}
@end
int main(intargc,const char* argv[]) {
@autoreleasepool{
ChainObject *c = [[ChainObject alloc]init];
c.name(@"小明").write(@"書法");
}
}
那么我們來思考一下,Block鏈?zhǔn)骄幊讨惺蚺校欠駮袃?nèi)存泄漏么豫尽?
這樣是不存在循環(huán)引用的。