最近在看Blocks Programming Topics中Creating a Block有如下的这段话:
If you don’t explicitly declare the return value of a block expression, it can be automatically inferred from the contents of the block. If the return type is inferred and the parameter list is
void
, then you can omit the (void
) parameter list as well. If or when multiple return statements are present, they must exactly match (using casting if necessary).
总共有三句话,分别字面上的翻译如下
- 如果你没有明确的声明一个block表达式的返回值,他可能会根据block的内容来推断出返回值。
- 如果返回类型是被推断出的并且参数列表是
void
,那么你同样可以省略(void
)参数列表。- 如果或者当多个返回表达式出现的时候,它们必须精确的匹配上(如果有必要的话使用强制转换)。
首先我是对block表达式的概念理解有误,所以做了如下的测试:
- (void)_incorrect_test
{
// 以下的例子都是错误的(编译报错),因为在定义变量的时候,必须要明确的指定返回类型
// 是block表达式中省略了返回值类型
// (^block1)(void) = ^(void) {
// return nil;
// };
//
// (^block2)(void) = id ^(void) {
// return nil;
// };
//
// (^block3)(void) = (id)^(void) {
// return nil;
// };
}
首先对于如下的一个block声明:
void* (^block2)(void) = ^(void) {
return nil;
};
=
左边的是变量声明,而=
右边的才是block expression
。
正确的理解了block expression
,做了如下的测试代码:
- (void)_correct_test
{
// 这个例子是错误的,因为nil在这里被理解为了 void*
// id (^block1)(void) = ^(void) {
// return nil;
// };
// 这样就可以了,定义变量的时候,让其返回void*
void* (^block2)(void) = ^(void) {
return nil;
};
// 在block表达式中,省略了返回值类型
id (^block3)(void) = ^(void) {
return [NSObject new];
};
// 在block表达式中,明确的指出了返回值类型
// 不是明确的指出了,是强制转为id类型
id (^block4)(void) = (id)^(void) {
return [NSObject new];
};
// 当参数列表是void的时候,在block表达式中可以省略
// 返回值类型是推断的 为void
void (^block5)(void) = ^{
NSLog(@"1");
};
// 当参数列表是void的时候,在block表达式中可以省略
// 返回值类型是推断的 为id
id (^block6)(void) = ^{
return [NSObject new];
};
// 当参数列表是void的时候,在block表达式中可以省略
// 返回值类型是指明的 为id
id (^block7)(void) = (id)^{
return [NSObject new];
};
id block8 = ^(int m) {
switch (m) {
case 1:
{
// 这里推断出block应该返回int
return 1;
}
break;
// case 2:
// {
// // Error: Return type 'NSObject *' must match previous return type 'int' when block literal has unspecified explicit return type
// return [NSObject new];
// }
// break;
case 3:
{
// 强行把double转换为整形,也许通常我们的做法是把case 1: 中返回int转换为返回double
return (int)(m + 4.0);
}
break;
// case 4:
// {
// // Error: Return type 'void *' must match previous return type 'int' when block literal has unspecified explicit return type
// return nil;
// }
// break;
default:
{
return 0;
}
break;
}
};
// block是一个对象,所以可以直接定义id block9
id block9 = ^(int m) {
return 5 + m;
};
// 正确的方法:显示的指明返回值类型
void (^block10)(void) = ^void(void) {
NSLog(@"11");
};
int (^block11)(int m) = ^int(int m) {
return m + 4;
};
int (^block12)(void) = ^int {
return 4;
};
block2();
block3();
block4();
block5();
block6();
block7();
int (^block_transf8)(int) = block8;
block_transf8(1);
int (^block_transf9)(int) = block9;
block_transf9(1);
block10();
block11(2);
block12();
id block20 = ^void (void) { printf("hello world\n"); };
id block21 = ^(void) { printf("hello world\n"); };
id block22 = ^{ printf("hello world\n"); };
id block23 = ^void { printf("hello world\n"); };
NSLog(@"%@, %@, %@, %@", block20, block21, block22, block23);
}
可以得知如下几个结论:
block1
和block2
可以得知,block
会把nil
推断为void *
而不是id
block3
和block4
可以得知,第一时间推断出可以在block expression
中指明返回类型,实际这个说法是错误的,前面的(id)
是把^{}
强制转换为id
的意思,而不是显式的说这个block(^{}
)的返回类型是id
。block5
,block6
,block7
,block12
可以得知,只要是参数列表为void
的时候,都可以省略,而不是文档中描述的,还需要是推断的返回类型(也许跟文档一直没有更新有关)。block20
,block21
,block22
,block23
。block8
是验证第三句话的。block9
是验证block
是一个对象这个结论的。block10
和block11
是显式的指明一个block的返回值类型
所以原文应该需要做如下的修改:
第一句话,应该是把返回值(return value
)改成返回类型(return type
)
第二句话,可以把the return type is inferred and
删除
有一个这样的网站:http://goshdarnblocksyntax.com/ 介绍了大部分的写法
现在列出如下:
As a local variable:(Demo1)
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
As a property:(Demo2)
@property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes);
As a method parameter:(Demo3)
- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;
As an argument to a method call:(Demo4)
[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];
As a typedef:(Demo5)
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};
首先我感觉还是缺少了一个例子:
As a Type:(Demo6)
returnType(^blockName)(parameterTypes) = (returnType(^)(parameterTypes))variable;
即对于上面的例子例子block8
来说就是:
int (^block_transf8)(int) = (int(^)(int))block8;
总结
可以根据如下两点:
- block的各种写法样例
- 把block想象成一个函数:
returnType functionName(paramTypes) {...}
可以上述所有的例子归纳成如下两种:
Block Syntax | format | Demo | desc |
---|---|---|---|
block type | returnType (^)(paramTypes) | Demo1的左边部分,Demo2,3,5,Demo6的左边和右边部分 | 一般类型(Demo6除外)后面都是有一个名字,所以直接在^ 后面加上名字就可以了 |
block expression | ^returnType(paramTypes) | Demo1的右边部分,Demo4 | 这里的returnType是可以省略的,paramTypes如果是void,那么(paramTypes) 也是可以省略的 |
在仔细看着两个format
就会发现,block type
的格式跟returnType functionName(paramTypes)
类似啊,只不过给^
加了()
,而block expression
是把returnType
和^
掉了个位置,去掉了^
的括号。
掌握了这些规矩,我觉得以后再写有关block的时候就不需要再去查相关网站了吧。
网友评论