可以用一句话来表示Blocks的扩充功能:带有自动变量(局部变量)的匿名函数。
1.Block语法
Block语法:^ 返回值类型 参数列表 表达式
例如可以写出如下形式的Block语法:
^int (int count){return count + 1;}
Block语法可以省略返回值类型: ^ 参数列表 表达式
胜率返回值类型时,如果表达式中有return语句就使用该返回值的类型,如果表达式中没有return语句就使用void类型。表达式中含有多个return语句时,所有return的返回值类型必须相同。
例如:
^(int count){return count + 1;}
返回值类型以及参数列表均可以省略:^ 表达式
例如:
^{NSLog("Blocks\n");}
2.Block类型变量
声明Block类型变量的示例如下:
int(^blk)(int)
可以作为以下用途使用:
自动变量
函数参数
静态变量
静态全局变量
全局变量
我们试着使用Block语法将Block赋值为Block类型变量:
int (^blk)(int) = ^(int count){return count + 1;}
也可以由Block类型变量向Block类型变量赋值:
int (^blk1)(int) = blk;
int (^blk2)(int);
blk2 = blk1;
在函数中使用Block类型变量可以向函数传递Block:
void func(int (^blk)(int))
{
//do something...
}
在函数返回值中指定Block类型,可以将Block作为函数的返回值返回。
我们可以像使用函数指针那样,使用typedef来解决问题:
typedef int (^blk_t)(int);
int result = blk(10);
Block类型变量可完全像通常的C语言变量一样使用,因此也可以使用指向Block类型变量的指针,即Block的指针类型变量:
typedef int (^blk_t)(int);
blk_t blk = ^(int count){return count + 1;};
blk_t *blkptr = &blk;
(*blkptr)(10);
3.截获自动变量值
“带自动变量值”在Blocks中表现为“截获自动变量值”。
例如:
int main()
{
int day = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (^blk)(void) = ^{print(fmt,val)};
val = 2;
fmt = "These values were changed. val = %d\n";
blk();
return 0;
}
执行结果是:
val = 10
因为执行Block表达式截获所使用的自动变量的瞬间值。
如果尝试改写截获的自动变量值,会产生编译错误:
int val = 0;
void (^blk)(void) = ^{val = 1};//产生编译错误
blk();
printf("val = %d\n",val);
4.__block说明符
若想在Block语法的表达式中奖值赋给在Block语法外声明的自动变量,需要在该自动变量上附加 __block说明符,就能实现在Block内赋值:
__block int val = 0;
void (^blk)(void) = ^{val = 1};
blk();
printf("val = %d\n",val);
执行结果:
val = 1
使用附有__block说明符的自动变量可以在Block中赋值,该变量称为__block变量。
5.截取的自动变量
这样不会产生编译错误:
id array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
id obj = [[NSObject alloc] init];
[array addObject:obj];//不会出错
};
这样就会产生错误:
<pre name="code" class="objc">__block id array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
array = [[NSMutableArray alloc] init];//编译错误
};
改成这样既可:
__block id array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
array = [[NSMutableArray alloc] init];//没有问题
};
另外,在使用C语言数组时必须小心使用其他指针:
const char text[] = "hello";
void (^blk)(void) = ^{
printf("%c\n",text[2]);//编译错误
};
这是因为在实现Blocks中,截获自动变量的方法并没有实现对C语言数组的截获。这时,使用指针可以解决该问题:
const char *text = "hello";
void (^blk)(void) = ^{
printf("%c\n",text[2]);
};