1.1 用途
闭包在很多语言中都有应用,它在OC中被叫做Blocks
,在Java中被叫做Lambda表达式
,也有直接叫做匿名函数的。
简单的说闭包就是一种带有局部变量的匿名函数。
在C语言中,函数可以通过函数名直接调用,也可以通过函数指针调用,但是这都需要开发者知道函数的名字(函数指针也需要知道函数名以便在被赋值时得到函数的地址)。
可能你会问,为什么要用闭包呢?一个常见的例子如下:
实现按钮的回调方法。
int buttonId = 0;
void buttonCallBack(int event){
NSLog(@"id = %d,event = %d",buttonId, event);
}
现在把情况扩展到多个按钮,如下:
void buttonCallBack(int buttonId, int event){
NSLog(@"id = %d,event = %d",buttonId, event);
}
void setButtonCallbacks(){ //工厂方法
for (int i = 0; i < MAX; i++) {
buttonId = i;
setButtonCallBack(i,&buttonCallBack); //省略单个set的方法了,只为说明思路
}
}
显然回调方法保存了按钮的ID以及回调函数的指针。闭包的出现可以使代码更加简洁,可以直接将回调卸载函数内,而不用再去写回调函数,例子如下:
void setButtonCallbacks(){
for (int i = 0; i < MAX; i++) {
setButtonCallbackUsingBlock(i,^(int event){
NSLog(@"id = %d,event = %d",buttonId, event);
});
}
}
注意:当用于函数参数时,Block 应该放在参数列表的最后一个。
下面介绍Blocks
的语法:
1.2 语法
Blocks
的语法有些晦涩,以至于有fuckingblocksyntax这个网站专门记录语法。
如下是Blocks
的语法:
^
返回值类型
参数列表
表达式
比如:
1
| ^int (int count){return count + 1;}
|
Blocks
是可以进行缩写的,如下
1.2.1 省略返回值类型
当省略返回值类型时,如果反表达式中又return
语句就使用该返回值的类型,如果表达式没有return
语句就是void
类型。
如果有多个return
语句,那么其类型必须相同。省略返回值类型后,例子如下:
1
| ^(int count){return count +1};
|
1.2.2 省略参数参数列表
如果不使用参数,参数列表也可以省略,例子如下:
1
| ^void (void) {NSLog(@"helloworld");}
|
可以省略 返回值类型
与参数列表
缩写为如下的形式:
1
| ^{NSLog(@"helloworld");}
|
1.2.2 Block 类型
与C语言中的变量相同,Block
类型的变量可以作一下用途。
- 局部变量
- 函数参数
- 静态变量
- 静态全局变量
- 全局变量
如下是一个常见的声明Block
类型的变量的例子:
1
| int (^blk) (int) = ^(int count){return count + 1;}
|
当然,在函数参数中使用Block
类型的变量就可以向函数传递Block
,在函数返回值中指定Block
类型,可以将Block
作为函数的返回值返回。分别对应如下的两个例子:
1
| void function (int (^blk) (int))
|
1 2 3 | int (^func())(int) { return ^(int count){return count + 1;} } |
到这里,Block
的语法变得着实复杂了,可以通过typedef
做简化。如下是简化的例子:
1 2 3 4 5 6 7 8 9 10 11 | typedef int (^blk_t) (int); //原来的写法 void func(int (^blk) (int)) //新的写法 void func(blk_t blk) //原来的写法 int (^func()(int)) //新的写法 blk_t func() |
1.2.3 捕获外部变量
Block
中捕获外部的局部变量具有瞬间性,即如果变量被Block
捕获后修改了值,那么Block
中捕获的变量的值并不会改变。
此外,Block
无法给捕获的外部变量赋值。
1.2.4 __block修饰符
Block
捕获外部的局部变量后,无法改变它的值,使用附有 __block
修饰符的局部变量可以在Block
中赋值。
1.2.5 注意事项
Block
中虽然无法给捕获的局部变量赋值,但是对于OC的对象的一些方法,是可以执行的,比如捕获一个NSMutableArray后,执行addObject
方法。这不会有任何问题,因为这相当于捕获了对象的实例指针。
对于C语言中的数组,Block
中并没有实现对之的捕获方法。可以使用指针来解决这个问题。
1 2 3 4 5 | char *text = "helloworld"; void (^blk)(void) = ^{ NSLog(@"%c",text[2]); } |