声明函数一样,需要包含返回值的类型,以及参数的类型,这样编译器才能安全的进行强
制类型转换。插入符
(^)跟指针(例如 int *aPointer)前面的星号(*)类似——只是在声明的时候
需要使用,之后用法跟普通的变量一样。
block 的定义本质上跟函数一样——只不过不需要函数名。block 以签名字符串开始:
^double(double rate, double time)标示返回一个 double,以及接收两个同样为 double 的参数
(如果不需要返回值,可以忽略掉)。在签名后面是一个大括弧({}),在这个括弧里面可以编
写任意的语句代码,这跟普通的函数一样。
当把 block 赋值给 distanceFromRateAndTime 后,我们就可以像调用函数一样调用这个变
量了。
不带参数的 Block
如果 block 不需要任何的参数,那么可以忽略掉参数列表。另外,在定义 block 的时候,
返回值的类型也是可选的,所以这样情况下,
block 可以简写为^ { … }:
double (^randomPercent)(void) = ^ {
return (double)arc4random() / 4294967295;
};
NSLog(@"Gas tank is %.1f%% full",
randomPercent() * 100);
在上面的代码中,利用内置的 arc4random()方法返回一个 32 位的整型随机数——为了获
得
0-1 之间的一个值,通过除以 arc4random()方法能够获取到的最大值(4294967295)。
到现在为止,block 看起来可能有点像利用一种复杂的方式来定义一个方法。事实上,
block 是被设计为闭包的(closure)——这就提供了一种新的、令人兴奋的编程方式。
Block 的闭包性(closure)
在 block 内部,可以像普通函数一样访问数据:局部变量、传递给 block 的参数,全局变
量
/函数。并且由于 block 具有闭包性,所以还能访问非局部变量(non-local variable)。非局部
变量定义在
block 之外,但是在 block 内部有它的作用域。例如,getFullCarName 可以使用
定义在
block 前面的 make 变量:
NSString *make = @"Honda";
NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
return [make stringByAppendingFormat:@" %@", model];
};
NSLog(@"%@", getFullCarName(@"Accord")); // Honda Accord
非局部变量会以 const 变量被拷贝并存储到 block 中,也就是说 block 对其是只读的。如果
尝试在
block 内部给 make 变量赋值,会抛出编译器错误。
以 const 拷贝的方式访问非局部变量,意味着 block 实际上并不是真正的访问了非局部变
量
——只不过在 block 中创建了非局部变量的一个快照。当定义 block 时,无论非局部变量
的值是什么,都将被冻结,并且
block 会一直使用这个值,即使在之后的代码中修改了非
局部变量的值。下面通过代码来看看,在创建好
block 之后,修改 make 变量的值,会发生
什么:
NSString *make = @"Honda";
NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
return [make stringByAppendingFormat:@" %@", model];
};
NSLog(@"%@", getFullCarName(@"Accord")); // Honda Accord
// Try changing the non-local variable (it won't change the block)