本质上是因为没有好的方法可以在 ARC 中转换以下代码:
CGColorRef a = ...;
id b = [(id)a autorelease];
CGColorRef c = (CGColorRef)b;
// do stuff with c
转换器删除-autorelease
并添加了一些桥接演员,但它卡住了:
CGColorRef a = ...;
id b = (__bridge_transfer id)a;
CGColorRef c = (__bridge_SOMETHING CGColorRef)b;
// do stuff with c. Except the compiler sees that b is no longer being used!
但是迁移者应该选择做__bridge_SOMETHING
什么呢?
- 如果选择
__bridge
, thenb
将不再使用,因此编译器可以立即释放它。这崩溃了。
- 如果选择
__bridge_retained
,则所有权将转移回“CF-land”,但原始代码假定该对象将归自动释放池所有。代码现在泄露了。
问题是 ARC 禁止调用-autorelease
,但没有记录的方法来保证将对象添加到自动释放池中——这样做的唯一充分理由是从方法中返回自动释放的 CF 类型,但是很多UIKit 类都有 CF -typed 属性(并且MKOverlayPathView
具有必须返回自动释放值的原子 CGPathRef
属性)。
这是我真正希望得到更好记录的 ARC 的棘手部分之一。
您可以跳过一些障碍,这些障碍可能会取得不同程度的成功。按照增加粘性的顺序:
CFAutorelease()
在未使用 ARC 编译的文件中定义函数(-fno-objc-arc
在目标设置 → 构建阶段 → 编译源中添加编译器标志)。我把这个作为练习留给读者。这是因为 ARC 代码需要与 MRC 代码互操作。这可能是最干净的解决方案。(这势必会引起评论说它不应该使用 CF 前缀,但只要你没有看到链接错误,C 符号名称冲突通常是安全的,因为引入了“两级命名空间”在 10.3 左右。)
各种箍向它发送-autorelease
消息或等效。所有这些都有些混乱,因为它们依赖于“愚弄”ARC,除了最后一个假设id
与 ABI 兼容的void*
. 它们也可能比上面的要慢,因为它们需要查找类/选择器(objc_lookUpClass()
并且sel_registerName()
可能更快甚至优化,但我不会打赌)。
return (__bridge CGColorRef)[(__bridge id)theColor performSelector:NSSelectorFromString(@"autorelease")]
[NSClassFromString(@"NSAutoreleasePool") addObject:(__bridge id)theColor]
return theColor;
return (__bridge CGColorRef)((id(*)(id,SEL))objc_msgSend)((__bridge id)theColor,NSSelectorFromString(@"autorelease"));
return ((void*(*)(void*,SEL))objc_msgSend)(theColor,NSSelectorFromString(@"autorelease"));
__autoreleasing
通过分配给编译器无法优化的变量来强制将其添加到自动释放池中。我不确定这是否得到保证(特别是类似于objc_autoreleaseReturnValue()
并且objc_retainAutoreleasedReturnValue()
可能是可能的,但我认为这不太可能,因为它会减慢 的常见情况(NSError * __autoreleasing *)error
)。
-(id)forceAutorelease:(id)o into:(id __autoreleasing*)p
{
*p = o;
return p;
}
-(CGColorRef)CGColor
{
...
CGColorRef theColor = CGColorCreate(...);
CGColorSpaceRelease(space);
id __autoreleasing temp;
return (__bridge CGColorRef)[self forceAutorelease:(__bridge_transfer id)theColor into:&temp];
}
(编译器/运行时也有可能合作并使用静态调度/内联,直到相关方法被覆盖,但这似乎很棘手,而且本身也有很大的开销。)
typedef
与 一起使用__attribute__((NSObject))
。这是ARC 规范中文档最容易混淆的部分,但类似这样的东西似乎有效:
typedef CGColorRef MyCGColorRef __attribute__((NSObject));
-(MyCGColorRef)CGColor
{
...
return (__bridge MyCGColorRef)(__bridge_transfer id)theColor;
}
我认为你需要两座桥梁才能工作(一个将所有权转移给 ARC,另一个转移到);如果你只是return theColor;
我怀疑它被泄露了。根据我对文档的阅读,您应该只需要(__bridge_transfer MyCGColorRef)
,因为它正在从非 ARC 指针 (CGColorRef) 转换为 ARC 指针 (MyCGColorRef),但这会让编译器抱怨。唉,文档没有给出如何使用__attribute__((NSObject))
typedef 的任何示例。
请注意,您不需要更改标头中的返回类型。这样做可能会启用自动释放的返回值优化,但我不确定编译器如何处理从 MyCGColorRef 到 CGColorRef 的转换。乐叹息。