【问题标题】:Call inline block specifying return Type and Parameters调用内联块指定返回类型和参数
【发布时间】:2011-09-01 18:07:06
【问题描述】:

我决定尝试在 Objective-C 中使用块进行控制流,但在调用多个内联块时遇到了一些问题。

我有一个 OOBoolean,它是 BOOL 原语的包装器,并提供以下方法:

+ (id) booleanWithBool: (BOOL) boolPrimitive;

- (id) initWithBool: (BOOL) boolPrimitive;

- (void) ifTrueDo: (void (^) ()) trueBlock 
        ifFalseDo: (void (^) ()) falseBlock;

- (void) ifTrueDo: (void (^) ()) trueBlock;

- (void) ifFalseDo: (void (^) ()) falseBlock;

像这样使用这个类没有问题:

OOBoolean* condition = [OOBoolean booleanWithBool: (1 + 1 == 2)];

id trueBlock = ^(){
    NSLog(@"True.");
};

id falseBlock = ^(){
    NSLog(@"False.");
};

[condition ifTrueDo: trueBlock ifFalseDo: falseBlock];

我得到“真”的结果。但是在尝试此操作时,我不断收到语法错误:

OOBoolean* condition = [OOBoolean booleanWithBool: (1 + 1 == 2)];

[condition ifTrueDo:(void (^)()) {
    NSLog(@"True");
} ifFalseDo:(void (^)()) {
    NSLog(@"False");
}];

难道不能匿名定义多个块并将它们传递给一个接受多个块参数的方法吗?如果是这样,那就有点令人失望了。

【问题讨论】:

  • 有趣的想法,虽然我认为这在运行时比让编译器为您优化条件要低得多。
  • 我实际上有兴趣了解这里的效率差异。显然,由于额外的消息发送,效率较低,而且我知道在 ifTrue ifFalse 消息调用之外有一些块和封闭变量的性能考虑,但我很好奇这会产生多大的影响。
  • 考虑这一点的最简单方法是考虑您要推送的堆栈帧数。您拥有的堆栈帧越多,您的代码效率就越低(可能)。我不太了解编译器理论,但我可以猜测使用标准 if() else 构造会在编译时使用某些选项优化程序流程。
  • 您是否熟悉其他类似的语言?你知道这些是否有类似的性能惩罚?我正在专门考虑 Smalltalk,但我想知道是否也可以针对这种格式进行编译器优化(我想它们必须在 Smalltalk 之类的东西中,因为它是进行控制流的唯一方法)。我不得不承认我对实现问题也很无知,我只是喜欢这种风格。
  • 我不能对 Smalltalk 发表评论(我知道你会提到 Smalltalk)但是任何值得一提的编译器都知道如何识别代码中的热点和常见的决策结构优化,就像展开循环一样。恕我直言,我认为 Objective C 编译器将无法优化您的 OOBolean 类行为。

标签: objective-c ios objective-c-blocks


【解决方案1】:

这是可能的。

你的括号太多了。试试这个:

[condition ifTrueDo:^() { NSLog(@"True"); } ifFalseDo:^() { NSLog(@"False"); } ];

编辑: 您的块语法略有错误。

如果你想包含返回类型和参数,你应该使用更接近这个的东西:

[self ifTrueDo:^ void (void) { NSLog(@"True"); } ifFalseDo:^ void (void) { NSLog(@"False"); } ];

英文:

^ [return type] ([parameter list]) {[block content]}

【讨论】:

  • 有趣。因此,如果您不使用详细的块定义样式( ^() { 块代码 } )它可以正常工作。但是做 void (^)() { block code} 是不行的吗?第一个选项适用于我的情况,但我认为在指定块时您还可以选择提供块返回类型和参数(在这种情况下为无)。
  • 更新了答案以包含块语法。
  • 你赢了。 ;) 我不知道如何在定义返回类型时显式设置它。我从来没有这样做过。我通常让编译器推断返回类型。另外,我尝试了显式设置返回类型的语法,它确实可以编译。
  • 是的,就是这样。混合了块的方法参数定义和内联规范样式。
  • @donalbain 原始代码的问题是在第一个花括号之前缺少^(void(^)()) { NSLog(...); } 应该是 (void(^)()) ^{ NSLog(...); }。所有这些其他建议,虽然是优秀的文体建议,但实际上并没有指出导致你如此困惑的具体错字。希望这可以帮助。 :)
【解决方案2】:

问题是您的方法声明需要一个返回 void(无)的块:

- (void) ifTrueDo: (void (^) ()) trueBlock 
        ifFalseDo: (void (^) ()) falseBlock;

但是,您稍后将其称为传入带有 (id^()()) 签名的块:

[condition ifTrueDo:(id (^)()) {
    NSLog(@"True");
}         ifFalseDo:(id (^)()) {
    NSLog(@"False");
}];

只需像下面这样去掉“id”部分 - 注意:我试过这个,它编译时没有警告:

[condition ifTrueDo:^{
          NSLog(@"True");
     }
     ifFalseDo:^{
          NSLog(@"False");
     }
 ];

【讨论】:

  • 我的错误,我忘记在发布代码之前将 id 更改为 void。现在应该反映当前代码。
  • 是的,确实如此。我认为我的问题是为什么当您另外指定块返回类型和块参数内联时它不会编译。所以 [condition ifTrueDo:(void)(^)(){ NSLog(@"True");...] 无法编译。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-30
  • 1970-01-01
  • 2019-12-14
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
相关资源
最近更新 更多