【问题标题】:Objective-C ARC and GCC ({}) extension compatibility?Objective-C ARC 和 GCC ({}) 扩展兼容性?
【发布时间】:2012-09-20 08:32:48
【问题描述】:

我正在将一些 Objective-C 代码转换为大量使用 GCC "Statements and Declarations in Expressions" extension ({}) 的 ARC。

GCC 扩展在预处理器宏中用于创建 Objective-C 对象。实际的宏相当复杂,所以这里是一个人为的例子来简化事情。在我的示例中假设_x 参数始终为NSString *

#define f(_x) ({ NSString *x = (_x); [NSString stringWithString: x]; })

这个宏当然会这样调用:

void foo() { NSString *s = f(@"foo"); }

在 ARC 之前的环境中,这可以正常工作。 stringWithString: 调用创建的对象是自动释放的对象,GCC 扩展将其分配给 s

在转换为 ARC (Xcode 4.4.1) 之后,当语句块退出时,对象会立即释放(我通过实例化 NSLog()s 它是 dealloc 的对象来确认这一点。

我尝试使用以下类型转换修改宏:

#define f(_x) ({ NSString *x = (_x); (__autoreleasing id) [NSString stringWithString: x]; })

#define f(_x) ({ NSString *x = (_x); (NSString __autoreleasing *) [NSString stringWithString: x]; })

但两种形式都会导致编译错误:

Explicit ownership qualifier on cast has no effect

那么看来 ARC 和这个 GCC 扩展是不兼容的。

是这样还是我错过了告诉 ARC 不要立即释放对象的简单内容?

我确实有一些选择,目前最吸引人的似乎是将所有这些宏转换为__inline__ 函数,但我有很多代码要处理。我真的希望能快速修复,因此任何想法都会受到赞赏。

【问题讨论】:

  • 坦率地说,我会硬着头皮把它们改成函数,这就是为什么它们一直应该是。

标签: objective-c gcc macros c-preprocessor automatic-ref-counting


【解决方案1】:

有趣的观察,可能值得一个编译器错误报告,因为结果显然与定义的语义相矛盾。

您可以将分配移动到您的宏中:

#define f(lhs, _x) ({ NSString *x = (_x); lhs = [NSString stringWithString: x]; })

这应该可以解决发布问题。但是切换到内联函数可能会更好!

【讨论】:

  • 不错的答案。我考虑将分配放在宏中,但必须更改数百个使用它的文件。这让我很紧张,这就是为什么我一直在寻找一个快速而肮脏的解决方案。
【解决方案2】:

我已经调试了几个小时并找到了解决方案,所以我会回答我自己的问题。 FWIW 我同意 CRD 并认为这是一个错误——我将向 Apple 提交报告。

碰巧我为我的问题编造的“人为的例子”确实有效(doh)。正如我最初提到的那样,我正在使用的宏要复杂得多,但事实并非如此。我发现,如果我通过临时返回创建的对象作为语句块中的最后一条语句,它们会。当这样写时,ARC不会释放对象。我假设 clang 中有一些非常窄的 RVO 类型代码,它与 ARC 解析器发生冲突。

无论如何,解决方法如下(滚动到行尾):

#define f(_x) ({ NSString *x = (_x); NSString *y = [NSString stringWithString: x]; y; })

【讨论】:

    【解决方案3】:

    PS:如果 ({}) 是我不完全了解的 GCC 特定宏扩展,那么请注意 Apple 现在默认使用 clang,而不是 GCC。

    【讨论】:

      【解决方案4】:

      问题是 {} 内的 NSString *x 未在 {} 外定义。所以以后不能引用它,ARC释放它是正确的。您的宏不应该首先工作,因为 { } 块的返回值在我看来是未定义的。

      这应该可以解决问题

      #define f(_x) (NSString *x = (_x); [NSString stringWithString: x]
      

      但由于命名和是多个语句,可能会产生副作用。

      使用内联函数是一种更合乎逻辑的方式。

      【讨论】:

      • ({}) 块的返回值在 GCC 中不是未定义的——它是一个文档化扩展。嵌入式语句块的目的是专门定义局部变量,因此宏参数不会计算两次。我会 -1 你的回答没有帮助(但不会因为你是新人),因为你完全错过了这一点......我了解机制,我知道我可以用内联函数等修复它。这个是一个记录在案的 GCC 扩展,似乎与 ARC 不兼容;我正在寻找澄清/验证/解决方法/等。关于那个问题。
      猜你喜欢
      • 2012-06-01
      • 2021-05-13
      • 1970-01-01
      • 2014-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-26
      • 1970-01-01
      相关资源
      最近更新 更多