1

我正在将一些 Objective-C 代码转换为 ARC,它大量使用了 GCC "Statements and Declarations in Expressions" 扩展 ({})

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

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

这个宏当然会像这样调用:

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

在 ARC 之前的环境中,这工作得很好。调用创建的对象stringWithString:是一个自动释放的对象,GCC 扩展将它分配给s.

NSLog()在转换为 ARC(Xcode 4.4.1)之后,当语句块退出时,对象会立即释放(我通过实例化一个s it'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__函数,但我有相当多的代码要处理。我真的希望能快速修复,所以任何想法都会受到赞赏。

4

4 回答 4

1

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

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

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

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

于 2012-09-20T10:29:40.923 回答
1

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

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

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

#define f(_x) ({ NSString *x = (_x); NSString *y = [NSString stringWithString: x]; y; })
于 2012-09-20T11:01:26.677 回答
0

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

这应该可以解决问题

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

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

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

于 2012-09-20T08:40:30.293 回答
0

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

于 2012-09-20T08:48:01.597 回答