Block语法
下面详细讲解一下带有局部变量值的匿名函数Block的语法。前面按钮回调例子中使用的Block语法如下:
^(int event) {
printf("buttonId:%d event=%d", i, event);
}
实际上,该Block语法使用了省略方式,其完整形式如下:
^void (int event) {
printf("buttonId:%d event=%d", i, event);
}
如上所示,完整形式的Block语法与一般的C语言函数定义相比,仅有两点不同。
① 没有函数名。
② 带有“^”。
第一点不同是没有函数名,因为它是匿名函数。第二点不同是返回值类型前带有“^”(插入记号,caret)记号。因为OS X、iOS应用程序的源代码中将大量Block,所以插入该记号便于查找。
Block语法如下:
^ 返回值类型 参数列表 表达式
“返回值类型”同C语言函数的返回值类型,“参数列表”同C语言函数的参数列表,“表达式”同C语言函数中允许使用的表达式。当然与C语言函数一样,表达式中含有return语句时,其类型必须与返回值类型相同。
例如可以写出如下形式的Block语法:
^int (int count){return count + 1;}
虽然前面出现过省略方法,但Block语法可省略好几个项目。首先是返回值类型:
^ 返回值类型 参数列表 表达式
省略后:
^ 参数列表 表达式
省略返回值类型时,如果表达式中有return语句就使用该返回值的类型,如果表达式中没有return语句就使用void类型。表达式中含有多个return语句时,所有return的返回值类型必须相同。前面的源代码省略其返回值类型时如下所示:
^(int count){return count + 1;}
该Block语法将按照return语句的类型,返回int型返回值。
其次,如果不使用参数,参数列表也可省略。以下为不使用参数的Block语法:
^void (void){printf(“Blocks”);}
该源代码可省略为如下形式:
^{printf(“Blocks”);}
返回值类型以及参数列表均被省略的Block语法是大家最为熟知的方式吧。
^ 返回值类型 参数列表 表达式
省略后
^ 表达式
Block类型变量
上面讲到Block语法单从其记述方式上来看,除了没有名称以及带有“^”以外,其他都与C语言函数定义相同。在定义C语言函数时,就可以将所定义函数的地址赋值给函数指针类型变量中。
int func(int count) {
return count + 1;
}
int (*funcptr)(int) = &func;
这样一来,函数func的地址就赋值给函数指针类型变量funcptr中了。
同样地,在Block语法下,可将Block语法赋值给声明为Block类型的变量中。即源代码中一旦使用Block语法就相当于生成了可赋值给Block类型变量的“值”。Blocks中由Block语法生成的值也被称为“Block”。在有关Blocks的文档中,“Block”既指源代码中的Block语法,也指由Block语法所生成的值。
声明Block类型变量的示例如下:
int (^blk)(int);
与前面的使用函数指针的源代码对比可知,声明Block类型变量仅仅是将声明函数指针类型变量的“*”变为“^”。该Block类型变量与一般的C语言变量完全相同,可作为以下用途使用。
- 自动变量
- 函数参数
- 静态变量
- 静态全局变量
- 全局变量
那么,我们就试着使用Block语法将Block赋值为Block类型变量。
int (^blk)(int) = ^(int count){return count + 1;};
由“^”开始的Block语法生成的Block被赋值赋值给变量blk中。因为与通常的变量相同,所以当然也可以由Block类型变量向Block类型变量赋值。
int (^blk1) (int) = blk;
int (^blk2)(int);
blk2 = blk1;
在函数参数中使用Block类型变量可以向函数传递Block。
void func(int (^blk)int)) {...
在函数返回值中指定Block类型,可以将Block作为函数的返回值返回。
int (^func()(int)) {
return ^(int count){return count + 1;};
}
由此可知,在函数参数和返回值中使用Block类型变量时,记述方式极为复杂。这里,我们可以像使用函数指针类型时那样,使用typedef来解决该问题。
typedef int (^blk_t)(int);
如下所示,通过使用typedef可声明“blk_t”类型变量。我们试着在以上例子中的函数参数和函数返回值部分里使用一下。
void func(int (^blk)(int)) //原来的记述方式
void func(blk_t blk)
int (^func()(int)) //原来的记述方式
blk_t func()
通过使用typedef,函数定义就变得更容易理解了。
另外,将赋值给Block类型变量中的Block方法像C语言通常的函数调用那样使用,这种方法与使用函数指针类型变量调用函数的方法几乎完全相同。变量funcptr为函数指针类型时,像下面这样调用函数指针类型变量:
int result = (*funcptr)(10);
变量blk为Block类型的情况下,这样调用Block类型变量:
int result = blk(10);
通过Block类型变量调用Block与C语言通常的函数调用没有区别。在函数参数中使用Block类型变量并在函数中执行Block的例子如下:
int func(blk_t blk, int rate) {
return blk(rate);
}
当然,在Objective-C的方法中也可使用。
- (int)methodsUsingBlock:(blk_t)blk rate:(int)rate {
return blk(rate);
}
Block类型变量可完全像通常的C语言变量一样使用,因此也可以使用指向Block类型变量的指针,即Block的指针类型变量。
typedef int (^blk_t) (int);
blk_t blk = ^(int count){return count + 1;};
blk_t *blkptr = &blk;
(*blkptr)(10);
由此可知Block类型变量可像C语言中其他类型变量一样使用。