Blocks模式
Block语法
下面我们详细讲解一下带有自动变量值得匿名函数Block的语法,即Block表达式语法。前面按钮回调例子中使用的Block语法如下:
^(int event) { printf("buttonId:%d event=%d\n", i, event); }实际上,该Block语法使用了省略方式,其完整形式如下:
^void (int event) { printf("buttonId:%d event=%d\n", i, event); }如上所示,完整形式的Block语法与一般的C语言函数相比,仅有两点不同:
(1)没有函数名。
(2)带有“^”。
第一点不同是没有函数名,因为他是匿名函数。第二点不同是返回值类型前带有“^”记号。因为OS X、iOS应用程序的源代码将大量使用Block,所以插入该记号便于查找。
以下为Block语法的BN范式:
Block_literal_expression ::= ^ block_decl compound_statement_body block_decl ::= block_decl ::= paramenter_list block_decl ::= type_expression即使此前不了解BN范式,通过说明也能有个概念。
^ 返回值类型 参数列表 表达式
“返回值类型”同C语言函数的返回值类型,“参数列表”同C语言函数的参数列表,“表达式”同C语言函数中允许使用的表达式。当然与C语言函数一样,表达式中含有return语句时,其类型必须与返回值类型相同。
例如可以写出如下形式的Block语法:
^int (int count) { return count + 1; }虽然前面出现过省略方式,但是Block语法可以省略好几个项目,首先是返回值类型。
^ 参数列表 表达式
省略返回值类型时,如果表达式中有return语句就使用该返回值的类型,如果表达式中没有return语句就使用viod类型。表达式中含有多个return语句时,所有return的返回值类型必须相同。前面的源代码省略其返回值类型时如下所示:
^(int count) { return count + 1; }该Block语法将按照return语句的类型,返回int型返回值。
其次,如果不使用参数,参数列表也可以省略。以下为不使用参数列表的Block语法:
^void (void) { printf("Blocks\n"); }该源代码可省略为如下形式:
^{printf("Blocks\n");}返回值类型以及参数列表均被省略的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 result1 = blk(10);通过Block类型变量调用Block与C语言通常的函数调用没有区别。在函数参数中使用Block类型变量并在函数中执行Block的例子如下:
int func(blk_t blk, int rate) { return blk(rate); }当然,在Objective-C的方法中也可以使用:
-(int)methodUsingBlock:(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语言中其他类型变量一样使用。