本系列文章主要是對《Objective-C 高級編程》這本書做的讀書筆記總結,除了這本書中的內容以外,也加上了自己對開發(fā)技術的理解和一些個人的經驗分享。
一、什么是Blocks
1.1 Blocks概要
Blocks是C語言的擴充功能,是帶有局部變量的匿名函數(匿名其實就是沒有名稱的函數,C語言標準不允許匿名函數)
例如:這里聲明了一個名稱為func的函數
int func (int count);
為了調用該函數,必須使用該函數的名稱
int result = func (10);
如果像下面這樣,使用函數指針來代替直接調用函數,那么不需要使用函數名也能夠使用該函數
int result = (*funcPtr) (10);
但其實使用函數指針也仍然需要知道函數名稱。例如以下,在賦值給函數指針時,若不使用函數的名稱,就取法取得該函數的地址。
int (*funcPtr)(int) = &func;
int result = (*funcPtr) (10);
而通過Blocks,源代碼中就能夠使用匿名函數。
匿名函數大家已經知道了,那么現在讓我們來看一下C語言中可能使用的變量有哪些 :
- 局部變量
- 函數參數
- 靜態(tài)全局變量
- 靜態(tài)局部變量
- 全局變量
1.2 Blocks語法
下面我們來詳細講解一下帶有局部變量值的匿名函數Blocks的語法:
^ 返回值類型 參數列表 表達式
以下定義表明這是一個表示沒有返回類型,并且參數為int型的Block
^ void (int a, int b) {
// do sth
}
如上所示:完整形式的Blocks和C語言的函數定義區(qū)別為:
- 沒有函數名 (匿名函數)
- 沒有
^
溫馨小貼士:因為OS X,iOS 應用程序的源代碼中大量使用Block,所以插入該記號便于查找。
block可以省略返回類型
省略返回類型的語法為: ^ 參數列表 表達式
^ void (int a, int b) {表達式}
等價于
^ (int a, int b) {表達式}
其次,如果不使用參數,block也可以省略參數列表
語法為: ^ 表達式
^ {
// 說點什么吧,少年
}
1.3 Block與C函數對比
int func (int count)
{
return count + 1;
}
// 函數func的地址賦值給函數指針變量funcPtr
// 在block語法下可將block賦值給聲明為block類型的變量
int (*funcPtr) (int) = &func
int (^blockName) (int)
與前面的使用函數指針的源代碼對比可知,聲明block類型變量僅僅是將聲明函數指針類型變量 * 變?yōu)?^
int (^block) (int) = ^ (int count) {
return count + 1;
}
block類型變量與c語言變量相同,block也可以作為函數參數傳遞或者函數的返回值
- 作為函數參數
void func ( int (^block) (int) ) {
//
}
- 作為函數的返回值
int (^func()) (int) {
//
return ^ (int count) {
return count + 1;
};
}
由上面源代碼可以看出在使用block變量時,記錄方式非常復雜。我們可以像使用函數指針類型那樣,使用 typedef來解決
typedef int (block_t) (int)
我們來對比以下
// 沒有定義前
void func ( int (^block) (int) ) {
}
// 定義后
void func (block_t block) {
}
另外,Block調用 和 C語言中使用函數指針調用函數的方法幾乎完全相同。
int result = (*funcPtr)(10);
int result = block(10);
也可以使用指向block類型變量的指針調用block
typedef int (^block_t) (int)
block_t block = ^ (int count) {
return count + 1;
}
block_t *blockPtr = █
(* blockPtr)(10);
1.4 截獲局部變量值以及__block說明符的使用
1.4.1 截獲局部變量值
截獲局部變量值是指保存執(zhí)行block語法瞬間的值,并且保存后就不能修改變量值。
int val = 0;
void (^block) (void) = ^ {
NSLog(@"val = %d", val);
}
val = 1;
block();
執(zhí)行上面源代碼,打印val的值為0。這是因為在block中截獲了局部變量的值,即保存了該變量的瞬間值。所以即使更改了變量的值也不會影響block的打印。
1.4.2__block說明符的使用
執(zhí)行下面源代碼,會產生編譯錯誤。
int val = 0;
void (^block) (void) = ^ {
val = 1;
}
block();
NSLog(@"val = %d", val);
向截獲的變量直接賦值會發(fā)生編譯錯誤,但使用截獲的值卻不會報錯。
id array = [NSMutableArray array];
void (^block) (void) = ^ {
id obj = [[NSObject alloc] init];
[array addObject:obj];
}
block();
若想在block中修改局部變量的值,需要在該自動變量前加 __block 說明符。
__block int val = 0;
void (^block) (void) = ^ {
val = 1;
}
block();
NSLog(@"val = %d", val);
源代碼的執(zhí)行結果為:val = 1