转换将自动释放的CGColor返回到ARC的方法

时间:2012-07-24 17:43:19

标签: objective-c xcode cocoa automatic-ref-counting core-foundation

我正在将我的项目转换为使用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可能泄漏对象

5 个答案:

答案 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:

  1. 在没有ARC编译的文件中定义CFAutorelease()函数(将-fno-objc-arc添加到目标设置中的编译器标志→构建阶段→编译源)。我将此作为练习留给读者。这是有效的,因为ARC代码需要与MRC代码互操作。这可能是最干净的解决方案。 (这必然会吸引评论说它不应该使用CF前缀,但只要你没有看到链接错误,C符号名称冲突通常是安全的,因为引入了“两级命名空间”在10.3左右。)

  2. 向其发送-autorelease消息或同等信息的各种箍。所有这些都有点乱,因为它们依赖于“愚弄”ARC,除了假设idvoid* 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"));
    
  3. 强制将其添加到自动释放池中,方法是指定编译器无法优化的__autoreleasing变量。我不确定这是否有保证(特别是类似于objc_autoreleaseReturnValue()objc_retainAutoreleasedReturnValue()的内容可能是可行的,但我认为这不太可能,因为它会减慢{{1}的常见情况})。

    (NSError * __autoreleasing *)error

    (编译器/运行时也可能合作并使用静态调度/内联,直到相关方法被覆盖,但这看起来很棘手,而且没有自己的大量开销。)

    < / LI>
  4. -(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)

事实上,在手动内存管理中,您可以retainreleaseautorelease任何 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。

Docs