文章目录
前言
一、Blocks概要
1.什么是Blocks
Blocks是C语言的扩充功能,并且可以用一句话来表示Blocks的扩充功能:Blocks是带有自动变量(局部变量)的匿名函数。
匿名函数:Blocks是一种匿名函数,也就是没有特定名称的函数。它们可以在需要的地方定义和使用,而无需提前声明或命名。这使得Blocks非常灵活,可以作为参数传递给其他函数或方法,或者作为变量保存和执行。
自动变量(局部变量):Blocks可以捕获其定义范围内的自动变量(也称为局部变量)。当一个Block被定义时,它会在其内部创建一个副本,用于在Block执行时访问该变量的值。这意味着即使变量超出了其定义范围,Block仍然可以访问和使用该变量的值。
闭包:由于Blocks可以捕获自动变量,它们形成了一个封闭的环境,即闭包。这意味着Block可以在其定义范围之外访问和使用自动变量。当Block被传递到其他函数或方法时,它会携带其封闭环境中的自动变量,以便在执行时可以访问这些变量的值。
二、Block模式
1.block语法
完整形式的Block语法和一般的C语言函数相比,只有两点不同。第一点是没有函数名,因为它是匿名函数。第二点是返回值类型前带有“^”(插入记号)记号。下面是Block语法格式:
^ 返回值类型 参数列表 表达式
^int (int count){
return count + 1;}
Block语法可以省略返回值类型
^(int count){
return count + 1;}
如果不使用参数,Block语法还可以省略参数列表
^void (void){
printf("Blocks\n");}
2.block类型变量
在C语言中可以将函数地址赋值给函数指针的类型变量中,同样在Block语法下,可将Block语法赋值给Block类型的变量中。示例如下:
int (^blk)(int) = ^(int count) {
return count + 1;};
int (^blk_t)(int);
blk_t = blk;
Block类型变量声明
返回参数 (^变量名称)(接受参数);
同时使用typedef重命名Block类型,因为Block类型变量和平时的使用类型相同,为了方便我们使用,我们通常都是用typedef来重命名Block。
typedef 返回参数 (^该Block的名称)(接收参数)
此时就可以用该Block的名称代表该Block了。
typedef int (^blk_t)(int)
blk_t blk = ^(int count) {
return count + 1;};
3.截获自动变量值
Block截获自动变量值就是说在定义Block的时候,其之前的变量都会被该Block所截获,该Block后再改变变量的值然后再调用,其Block中所捕获的值不会受到改变。
int main(int argc, const char * argv[]) {
int dmy = 256;
int val = 10;
const char *fmt = "val = %d\n";
void (^blk)(void) = ^{
printf(fmt, val);
};
val = 2;
fmt = "These values were changed. val = %d\n";
blk();
return 0;
}
此时我们发现,在定义Block之后的这val,fmt两个变量被改变了,但是再次调用该Block还是定义Block之前的值,这就是捕获。我的理解就是定义Block之前的自动变量都会被该Block捕获,在定义Block后改变变量其值还是该Block之前的定义的值,不会收到影响。
4._Block修饰符
自动变量截获只能保存执行Block语法瞬间的值并且保存后就不能修改这个值,如果要修改这个自动变量的值就需要在在声明时给这个自动变量附加_Block说明符。
int main(int argc, const char * argv[]) {
__block int val = 10;
__block const char *fmt = "val = %d\n";
void (^blk)(void) = ^{
printf(fmt, val);
val = 20;
};
val = 2;
fmt = "These values were changed. val = %d\n";
blk();
printf(fmt, val);
return 0;
}
5.截获的自动变量
前面提到向Block截获的自动变量赋值会报错,如果调用变更该对象的方法则不会产生编译错误。
用OC中的NSMutableArray来说,截获它其实就是截获该类对象用的结构体实例指针,就是说你只要不改变其指针的指向和其指针的值,他就不会出错。
int main(int argc, const char * argv[]) {
id array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
id obj = [[NSObject alloc] init];
[array addObject:obj];
};
blk();
return 0;
}
这里因为你并没有改变其array指针的指向和值,而只是在其地址后边新加了一个值,所以他就不会报错。
int main(int argc, const char * argv[