Block几种写法的总结

最近在看Blocks Programming TopicsCreating 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).

总共有三句话,分别字面上的翻译如下

  1. 如果你没有明确的声明一个block表达式的返回值,他可能会根据block的内容来推断出返回值。
  2. 如果返回类型是被推断出的并且参数列表是void,那么你同样可以省略(void)参数列表。
  3. 如果或者当多个返回表达式出现的时候,它们必须精确的匹配上(如果有必要的话使用强制转换)。

首先我是对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);
}

可以得知如下几个结论:

  1. block1block2可以得知,block会把nil推断为void *而不是id
  2. block3block4可以得知,第一时间推断出可以在block expression中指明返回类型,实际这个说法是错误的,前面的(id)是把^{}强制转换为id的意思,而不是显式的说这个block(^{})的返回类型是id
  3. block5,block6,block7,block12可以得知,只要是参数列表为void的时候,都可以省略,而不是文档中描述的,还需要是推断的返回类型(也许跟文档一直没有更新有关)。block20,block21,block22,block23
  4. block8是验证第三句话的。
  5. block9是验证 block是一个对象这个结论的。
  6. block10block11是显式的指明一个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的时候就不需要再去查相关网站了吧。

猜你喜欢

转载自blog.csdn.net/weixin_34245082/article/details/87573779