我正在将我的项目转换为使用ARC。我在NSColor上有一个类别,它有一个返回自动释放的CGColor表示的方法:
@implementation NSColor (MyCategory)
- (CGColorRef)CGColor
{
NSColor *colorRGB = [self colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
CGFloat components[4];
[colorRGB getRed:&components[0]
green:&components[1]
blue:&components[2]
alpha:&components[3]];
CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGColorRef theColor = CGColorCreate(space, components);
CGColorSpaceRelease(space);
return (CGColorRef)[(id)theColor autorelease];
}
@end
使用ARC执行此操作的正确方法是什么?我不想返回保留的CGColor。
XCode中的ARC转换器建议使用
return (CGColorRef)[(__bridge id)theColor autorelease];
但是会导致以下错误消息:
[rewriter]投射到'CGColorRef'的结果是不安全的 'autorelease'消息; __bridge强制转换可能会导致指向a 被破坏的对象和__bridge_retained可能泄漏对象
答案 0 :(得分:8)
基本上是因为没有好的方法来转换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
,则不再使用b
,因此编译器可以立即释放它。这崩溃了。__bridge_retained
,则所有权将返回转移到“CF-land”,但原始代码假定该对象将由自动释放池拥有。代码现在泄密了。问题是ARC禁止调用-autorelease
但是没有文档化的方法来保证将对象添加到自动释放池中 - 唯一的理由是从方法返回自动释放的CF类型,但很多的UIKit类都有CF类型的属性(MKOverlayPathView
有一个原子 CGPathRef
属性必须返回自动释放值。)
这是我真正希望更好地记录的ARC的一个棘手问题。
你可以跳过一些可能会有不同程度成功的箍。为了增加ickiness:
在没有ARC编译的文件中定义CFAutorelease()
函数(将-fno-objc-arc
添加到目标设置中的编译器标志→构建阶段→编译源)。我将此作为练习留给读者。这是有效的,因为ARC代码需要与MRC代码互操作。这可能是最干净的解决方案。 (这必然会吸引评论说它不应该使用CF前缀,但只要你没有看到链接错误,C符号名称冲突通常是安全的,因为引入了“两级命名空间”在10.3左右。)
向其发送-autorelease
消息或同等信息的各种箍。所有这些都有点乱,因为它们依赖于“愚弄”ARC,除了假设id
与void*
ABI兼容的最后一个。它们也可能比上面的要慢,因为它们需要查找一个类/选择器(objc_lookUpClass()
和sel_registerName()
可能会更快或甚至优化掉,但我不会赌它。) / p>
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()
的内容可能是可行的,但我认为这不太可能,因为它会减慢{{1}的常见情况})。
(NSError * __autoreleasing *)error
(编译器/运行时也可能合作并使用静态调度/内联,直到相关方法被覆盖,但这看起来很棘手,而且没有自己的大量开销。)
< / LI> -(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
一起使用。这是ARC spec中最容易引起混淆的部分,但像这样的似乎工作:
__attribute__((NSObject))
我认为你需要两个桥梁才能工作(一个将所有权转移到ARC而另一个转移到);如果你只是typedef CGColorRef MyCGColorRef __attribute__((NSObject));
-(MyCGColorRef)CGColor
{
...
return (__bridge MyCGColorRef)(__bridge_transfer id)theColor;
}
我怀疑它已经泄露了。从我阅读文档开始,你应该只需要return theColor;
,因为它是从非ARC指针(CGColorRef)转换为ARC指针(MyCGColorRef),但这会让编译器抱怨。唉,文档没有给出如何使用(__bridge_transfer MyCGColorRef)
typedef的任何示例。
请注意,您无需更改标题中的返回类型。这样做可能会启用自动释放的返回值优化,但我不确定编译器如何处理从MyCGColorRef到CGColorRef的转换。 Le叹息。
答案 1 :(得分:7)
CGColor
是Core Foundation对象。您不应该尝试使用autorelease
。相反,您应该重命名方法copyCGColor
并返回保留的对象。
自动发布是Objective-C的概念。它在核心基金会层面不存在。
由于CGColor
没有免费桥接到任何Objective-C类,因此尝试自动释放它是非常奇怪的(即使这可能有效)。
几年后更新
现在CFFutorelease()处于CoreFoundation级别(自Mavericks和iOS 7以来可用)。
答案 2 :(得分:4)
从OS X 10.9或iOS 7开始,您只需使用CFAutorelease()
(在CFBase.h中声明)。
答案 3 :(得分:1)
事实上,在手动内存管理中,您可以retain
,release
和autorelease
任何 CoreFoundation 对象,因为它们都是免费桥接至少NSObject
。
由于ARC禁止使用手动内存管理,我们应该以某种方式告诉提示编译器要做什么。一种方法是命名方法- (CGColorRef)copyCGColor;
,以便编译器知道该方法返回具有+1保留计数的对象。
但是如果你像我一样并且更喜欢这种方法的简单“CGColor”,你可以将__attribute__((cf_returns_retained))
附加到方法定义中:
@interface NSColor (MyCategory)
- (CGColorRef)CGColor __attribute__((cf_returns_retained));
@end
答案 4 :(得分:0)
我认为你想在这种情况下使用__bridge_transfer。