Objective-C中的代码块(Block)是iOS4.0和Mac OS X 10.6之后的系统中引进的对C语言的扩展。
它在Xcode和Clang编译器中是有效的,但它并不属于ANSI C标准。
代码块对象(通常称为代码块)除了函数中的代码,还包含变量绑定。代码块有时也称为闭包(closure)。
代码块包含两种类型的绑定:
- 自动型:自动绑定(automatic binding)使用的是栈内存;
- 托管型:托管绑定(managed binding)使用的是堆内存。
关于代码块的内存绑定未来会有更详细的笔记。
因为代码块实际上是由C语言实现的,所以它们在各种以C作为基础的语言内都是有效的,包括Objective-C、C++以及Objective-C++。
一般可以用以下关系来表示一个代码块:
(^blockname)(list of arguments) = ^(arguments){body;};
代码块通常显得简洁明了:
- 编译器可以通过代码块的内容推导出返回类型,所以返回类型
是可以省略的; - 如果代码块没有参数arguments,那也可以省略。
最简化的示例如下:
void (^theBlock)() = ^{printf("Hello Block!\n");};
可以像使用函数一样使用代码块。
调用代码块的时候不需要使用幂符号(^),只有声明代码块的时候才需要:
int result = square_block(5);
使用代码块时通常不需要创建一个代码块变量,而是在代码内联代码块的内容。
可以在需要代码块做参数的方法或函数里面直接使用:
NSArray *array = [NSArray arrayWithObjects:
@"Amir", @"Mishal", @"Irrum", @"Adam", nil];
NSLog(@"Unsorted Array %@", array);
NSArray *sortedArray = [ array sortedArrayUsingComparator:
^(NSString *object1, NSString *object2) {
return [object1 compare:object2];
}//Using a Block Directly
];
NSLog(@"Sorted Array %@", sortedArray);
代码块在定义的时候经常会比较长,容易出错,此时可以用typedef帮忙:
typedef double (^MKSampleMultiply2BlockRef)(double c, double d);
这行代码定义了一个名为MKSampleMultiply2BlockRef的代码块变量类型,它包含两个double型的参数,并且返回一个double型的数值。
有了typedef,就可以像下面这样使用这个代码块变量:
#include
typedef double (^MKSampleMultiply2BlockRef)(double c, double d);
int main(int argc, const char * argv[])
{
MKSampleMultiply2BlockRef multiply2 = ^(double c, double d) { return c * d; };
printf("%f, %f", multiply2(4, 5), multiply2(5, 2));
return 0;
}
代码块被声明后会捕捉创建点的状态。代码块可以访问函数用到的标准类型变量:
本地变量就是与代码块在同一范围内生命的变量:
typedef double(^MKSampleMultiplyBlockRef)(void);
double a = 10,b = 20;
MKSampleMultiplyBlockRef multply = ^(void){return a*b;};
NSLog(@"%f",multiply()); //200
a = 20;
b = 50;
NSLog(@"%f",multiply()); //200
运行后可以看到该段代码的两个NSLog语句都输出200.00,为什么呢?
因为代码块会在定义时复制并保存本地变量的状态。
在上面的本地变量示例中,我们说过变量与代码块拥有相同的有效范围,所以可以根据需要把变量标记成静态的(全局的):
static double a = 10, b = 20;
MKSampleMultiplyBlockRef multiply = ^(void){ return a * b; };
NSLog(@"%f", multiply()); //200
a = 20;
b = 50;
NSLog(@"%f", multiply()); //1000
运行后,第一个NSLog语句输出200.00,第二个NSLog语句输出1000.00。
因为代码块保留的是全局变量的a和b,第二个NSLog语句执行之前,全部变量a和b的值已经被修改,所以输出也随之改变。
代码块中的参数变量与函数中的参数变量具有相同的作用:
typedef double (^MKSampleMultiply2BlockRef)(double c, double d);
MKSampleMultiply2BlockRef multiply2 = ^(double c, double d) { return c * d; };
NSLog(@"%f, %f", multiply2(4, 5), multiply2(5, 2));
//Output:
//More Typedefs[1818:30515] 20.000000, 10.000000
本地变量会被代码块作为常量获取到。
如果想要修改它们的值,必须把它们声明为可修改的,否则将出现编译错误:
//the following code will fail to compile:
double c = 3;
MKSampleMultiplyBlockRef multiply = ^(double a, double b) { c = a * b; };
//To fix the compilation error:
__block double c = 3;
MKSampleMultiplyBlockRef multiply = ^(double a, double b)
{ c = a * b; };
以下变量是无法声明为__block类型的:
- 没有长度可变的数组;
- 没有包含可变长度数组的结构体。
代码块内部的本地变量与函数中的本地变量具有同样的作用:
#include
typedef void (^MKSampleMultiplyBlockRef) (double a, double b);
int main(int argc, const char * argv[])
{
__block double c = 3;
MKSampleMultiplyBlockRef multiply = ^(double a, double b) { c = a * b; };
multiply(4, 5); //a = 4, b = 5
printf("c = %f\n", c);
void (^MKSampleBlock)(void) = ^(void) {
double a = 4; //参数列表为空,但在代码块内部定义了变量
double c = 2;
printf("a * c = %f\n", a * c);
};
MKSampleBlock(); //执行代码块,效果与本地变量相同
return 0;
}
//Output:
//c = 20.000000
//a * c = 8.000000
//Program ended with exit code: 0