我正在将一些Objective-C代码转换为ARC,大量使用GCC "Statements and Declarations in Expressions" extension ({})
。
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()
它的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__
函数,但我有相当多的代码需要处理。我真的希望快速解决,所以任何想法都会受到赞赏。
答案 0 :(得分:1)
有趣的观察,可能值得编译错误报告,因为结果明显与定义的语义相反。
您可以将作业移动到宏中:
#define f(lhs, _x) ({ NSString *x = (_x); lhs = [NSString stringWithString: x]; })
应解决发布问题。但是切换到内联函数可能更好!
答案 1 :(得分:1)
我已经调试了几个小时了,找到了解决方案,所以我会回答我自己的问题。 FWIW我同意CRD并认为这是一个错误 - 我会向Apple提交一份报告。
碰巧我为我的问题做出的“人为的例子”确实有效(doh)。正如我之前提到的那样,我正在使用的宏更加复杂,但它们却没有。我发现,如果我通过临时语句返回创建的对象作为语句块中的最后一个语句,它们将。当写入时,ARC不会释放该对象。我假设clang中有一些非常窄的RVO类型代码,它与ARC解析器发生冲突。
无论如何,解决方法看起来都是这样(滚动到该行的最后):
#define f(_x) ({ NSString *x = (_x); NSString *y = [NSString stringWithString: x]; y; })
答案 2 :(得分:0)
问题是{}内的NSString * x未在{}之外定义。所以事后不能引用它,ARC释放它是正确的。你的宏不应该首先工作,因为{}块的返回值在我眼中是不确定的。
这应该可以解决问题
#define f(_x) (NSString *x = (_x); [NSString stringWithString: x]
但由于命名和多个陈述可能会产生副作用。
使用内联函数是一种更合乎逻辑的方法。
答案 3 :(得分:0)
PS:如果({})是GCC特定的宏扩展,我并不完全清楚,那么请注意Apple现在使用clang而不是GCC作为默认值。