Dru*_*erB 11 xcode cocoa objective-c core-foundation automatic-ref-counting
我正在将我的项目转换为使用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
Run Code Online (Sandbox Code Playgroud)
使用ARC执行此操作的正确方法是什么?我不想返回保留的CGColor.
XCode中的ARC转换器建议使用
return (CGColorRef)[(__bridge id)theColor autorelease];
Run Code Online (Sandbox Code Playgroud)
但这会导致以下错误消息:
[rewriter]将'autorelease'消息的结果转换为'CGColorRef'是不安全的; __bridge强制转换可能会导致指向被破坏对象的指针,并且__bridge_retained可能会泄漏对象
基本上是因为没有好的方法来转换ARC中的以下代码:
CGColorRef a = ...;
id b = [(id)a autorelease];
CGColorRef c = (CGColorRef)b;
// do stuff with c
Run Code Online (Sandbox Code Playgroud)
转换器移除-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!
Run Code Online (Sandbox Code Playgroud)
但是迁移者应该选择做__bridge_SOMETHING什么呢?
__bridge,则b不再使用,因此编译器可以立即释放它.这崩溃了.__bridge_retained,则所有权转移回 "CF-land",但原始代码假定该对象将由自动释放池拥有.代码现在泄密了.问题是ARC禁止调用,-autorelease但是没有文档化的方法来保证将对象添加到自动释放池中 - 这是从方法返回自动释放的CF类型的唯一好理由,但是很多 UIKit类都有CF -typed属性(并且MKOverlayPathView具有必须返回自动释放值的原子 CGPathRef属性).
这是我真正希望更好地记录的ARC的一个棘手问题.
你可以跳过一些可能会有不同程度成功的箍.为了增加ickiness:
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"));
Run Code Online (Sandbox Code Playgroud)通过分配__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];
}
Run Code Online (Sandbox Code Playgroud)
(在重写相关方法之前,编译器/运行时也可以合作并使用静态调度/内联,但这看起来很棘手,而且没有自己的大量开销.)
使用typedef带__attribute__((NSObject)).这是ARC规范中最容易混淆的部分,但是这样的东西似乎有效:
typedef CGColorRef MyCGColorRef __attribute__((NSObject));
-(MyCGColorRef)CGColor
{
...
return (__bridge MyCGColorRef)(__bridge_transfer id)theColor;
}
Run Code Online (Sandbox Code Playgroud)
我认为你需要两个桥梁才能工作(一个将所有权转让给ARC而另一个转让给它); 如果你只是return theColor;我怀疑它被泄露了.从我对文档的阅读中,您应该只需要(__bridge_transfer MyCGColorRef)因为它从非ARC指针(CGColorRef)转换为ARC指针(MyCGColorRef),但这会让编译器抱怨.唉,文档没有给出如何使用__attribute__((NSObject))typedef的任何示例.
请注意,您无需更改标题中的返回类型.这样做可能会启用自动释放的返回值优化,但我不确定编译器如何处理从MyCGColorRef到CGColorRef的转换.叹了口气.
CGColor是Core Foundation对象.你不应该尝试使用autorelease它.相反,您应该重命名方法copyCGColor并返回保留的对象.
自动发布是Objective-C的概念.它在核心基金会层面不存在.
由于CGColor没有免费桥接到任何Objective-C类,尝试自动释放它是非常奇怪的(即使这可能有效).
几年后更新
现在CFFutorelease()处于CoreFoundation级别(自Mavericks和iOS 7以来可用).
| 归档时间: |
|
| 查看次数: |
3012 次 |
| 最近记录: |