Block
Block作為日常開(kāi)發(fā)中必備的一種開(kāi)發(fā)技巧,通過(guò)日常的使用和學(xué)習(xí),在此對(duì) Block的基本概念,使用語(yǔ)法和 對(duì)于外部變量的使用三方面做一個(gè)學(xué)習(xí)總結(jié)。
1.什么是Blocks
什么是blocks,blocks是c語(yǔ)言的一種擴(kuò)充功能。可以解釋為“帶有自動(dòng)變量的匿名函數(shù)”。
匿名函數(shù)
匿名函數(shù)就是指沒(méi)有名稱的函數(shù)。正常的函數(shù)表達(dá)式一般為:
返回值類型 函數(shù)名 返回值 {
}
void func () {
}
而當(dāng)我門需要調(diào)用這個(gè)函數(shù)時(shí),是必須要知道函數(shù)名的。通常調(diào)用函數(shù)的方式有2種:
1 函數(shù)名()
2 函數(shù)指針的方式調(diào)用
// 1 函數(shù)名調(diào)用
func();
// 2 函數(shù)指針調(diào)用
void(*funcptr)() = &func;
funcptr();
而blocks的函數(shù)表達(dá)式并沒(méi)有帶函數(shù)名:
^ 返回值 參數(shù) {
}
^ void(){
};
把blocks賦值給block類型的參數(shù)
void(^blk)() = ^ void(){
};
調(diào)用blocks
blk();
或者
^ void(){
}();
可以發(fā)現(xiàn)blocks函數(shù)與常見(jiàn)的函數(shù)的區(qū)別 是沒(méi)有了函數(shù)名和 最前面加上了** ^ *符號(hào)。而block類型的變量和函數(shù)指針的區(qū)別只是把函數(shù)指針的號(hào)換成了^。
截獲自動(dòng)變量
帶有自動(dòng)變量其實(shí)可以解釋為能夠截取自動(dòng)變量的值。通常的變量類型有
- 局部變量(自動(dòng)變量)
- 全局變量
- 全局靜態(tài)變量
- 靜態(tài)變量
能夠在函數(shù)的多次調(diào)用的時(shí)候傳遞的變量值只有:
- 全局變量
- 全局靜態(tài)變量
- 靜態(tài)變量
void test() {
int a = 10;
void(^blk)() = ^void(){
printf("a = %d",a);
};
a = 20;
blk();
}
函數(shù)最后的打印結(jié)果為*** a = 10 ***。到這里可以解釋出blocks為帶有(截獲)自動(dòng)變量i的匿名函數(shù)。
2.Block語(yǔ)法
block函數(shù)
關(guān)于block函數(shù)可以表示為 ^ 返回值類型 參數(shù) 表達(dá)式。
而以下4種方式也是可以的
1.^ 返回值類型 參數(shù) 表達(dá)式
^int(){
return 1;
};
2.^ 參數(shù) 表達(dá)式 (省略返回值類型)
^(){
return 1;
};
3.^ 返回值類型 表達(dá)式 (在沒(méi)有參數(shù)的情況下省略參數(shù))
^int{
return 1;
};
4.^ 表達(dá)式 (返回值類型和參數(shù) 都可以省略)
^{
return 1;
};
block類型的變量
在之前介紹過(guò)block類型的變量,將block函數(shù)可以賦值給block類型變量。它和函數(shù)指針十分相似。
// 函數(shù)指針
void func() {
}
void (*funcptr)() = &func;
//block類型變量
void (^blk)() = ^{
};
在日常開(kāi)發(fā)中,我門會(huì)經(jīng)常使用到block,關(guān)于block 它可以作為
- block類型變量
- 屬性
- 方法參數(shù)
- 方法返回值
// 1 block作為變量
void (^blk)() = ^{
};
// 2 block作為屬性
@property(nonatomic,copy) void(^blk)();
//3 block作為方法參數(shù)
- (void)methodUseBlock:(void (^)())blk {
}
//4 block作為方法返回值
- (void(^)())methodReturnBlock {
return ^{};
}
平時(shí)為了更方便的使用block,將這種復(fù)雜的記述方式便于使用,可以借助typedef來(lái)使用block
typedef void (^blkType)();
通過(guò)使用typedef,再來(lái)看下實(shí)現(xiàn)之前的block場(chǎng)景
typedef void (^blkType)();
// 1 block作為變量
blkType blk = ^void(){
};
// 2 block作為屬性
@property(nonatomic,copy) blkType blk;
//3 block作為方法參數(shù)
- (void)methodUseBlock:(blkType)blk {
}
//4 block作為方法返回值
- (blkType)methodReturnBlock {
return ^{};
}
平時(shí)方便查閱的話,可以到這里看下 http://fuckingblocksyntax.com/
3.截獲的外部變量
保存定義時(shí)的值
在之前介紹block函數(shù)概念的時(shí)候,說(shuō)了block能夠截獲外部變量。而且是在定義block函數(shù)的時(shí)候截獲了外部的變量.對(duì)于局部變量來(lái)說(shuō)在定義的block函數(shù)之后,改變局部變量的值。block內(nèi)部使用的局部變量的值依然是block函數(shù)定義時(shí)的值。如下面的代碼 函數(shù)最后的執(zhí)行結(jié)果依然是 *** a = 10 ***.
void test() {
int a = 10;
void(^blk)() = ^void(){
printf("a = %d",a);
};
a = 20;
blk();
}
在block內(nèi)部改變外部變量
如果block內(nèi)部使用了外部變量,是不允許在block內(nèi)部修改截獲的變量的值的。所以下面的這段代碼就是編譯不過(guò)的。因?yàn)樗淖兞私孬@的外部變量a的值。
int a = 10;
void(^blk)() = ^{
a = 20;
};
但是下面的這段代碼卻是可以正常編譯的。
NSMutableArray *tempArray = [NSMutableArray array];
void(^blk)() = ^{
[tempArray arrayByAddingObject:[[NSObject alloc]init]];
};
在這段代碼中,blk確實(shí)截獲了外部變量 tempArray這個(gè)數(shù)組,在block內(nèi)部也向數(shù)組里增加了一個(gè)元素。為什么它能編譯通過(guò)呢。其實(shí)block內(nèi)部不允許改變截獲的外部變量應(yīng)該更嚴(yán)格的說(shuō)是不允許改變外部變量的內(nèi)存地址。
NSMutableArray *tempArray = [NSMutableArray array];
NSLog(@"原始的內(nèi)存地址 %p",&tempArray);
[tempArray arrayByAddingObject:[[NSObject alloc]init]];
NSLog(@"添加元素后的內(nèi)存地址 %p",&tempArray);
打印結(jié)果
2016-12-18 16:40:19.565 block[8358:803598] 原始的內(nèi)存地址 0x7fff5778cac8
2016-12-18 16:40:19.565 block[8358:803598] 添加元素后的內(nèi)存地址 0x7fff5778cac8
可以看出內(nèi)存地址并沒(méi)有改變,而如果對(duì)對(duì)象進(jìn)行賦值操作,則會(huì)和之前一樣直接編譯錯(cuò)誤。因?yàn)楦淖兞送獠孔兞康膬?nèi)存地址。
NSMutableArray *tempArray = [NSMutableArray array];
void(^blk)() = ^{
tempArray = [NSMutableArray arrayWithObjects:[[NSObject alloc]init], nil];
};
__block說(shuō)明符
既然在block內(nèi)部無(wú)法直接修改截獲的外部變量的值,而如果我門需要這樣的功能的話,可以通過(guò)對(duì)外部變量加上 _ _ block修飾符則能實(shí)現(xiàn)在block內(nèi)部修改外部變量的值。如下代碼通過(guò)對(duì)外部變量a加上 _ _block 修飾符,就算在block內(nèi)部修改了a的值,也能正常編譯。
__block int a = 10;
void(^blk)() = ^{
a = 20;
};
blk();