NSData/CFData 桥接 @autorelease 但不是 ARC
Posted
技术标签:
【中文标题】NSData/CFData 桥接 @autorelease 但不是 ARC【英文标题】:NSData/CFData bridging with @autorelease but not ARC 【发布时间】:2016-04-25 23:59:50 【问题描述】:我有一个不使用 ARC 的库。
简化后的代码如下所示:
//
// test.m
// test-cast
//
#import <Foundation/Foundation.h>
static
CFDataRef CFDataCreateFromResource(NSString *name)
NSURL *url = [[NSBundle mainBundle] URLForResource:name withExtension:@".bin"];
NSData *binData = [NSData dataWithContentsOfURL:url];
return (CFDataRef) binData;
void test(void)
CFDataRef data = CFDataCreateFromResource(@"Data");
if(data) CFRelease(data);
这是一个简化版本。测试中的代码是现有的并且要复杂得多。它手动使用各种 CFType 和 CFRelease。它最初是 C 代码。
我正在添加 CFDataCreateFromResource 代码并在 test() 中调用它的几行代码,在此过程中将文件从 C 更改为 Objective-C。
这个(静态)库是从一个应用程序调用的,它将对 test() 的调用包装在一个 @autoreleasepool 块中:
int main(int argc, const char * argv[])
@autoreleasepool
// insert code here...
NSLog(@"Hello, World!");
test();
NSLog(@"All done!");
return 0;
这里的问题是程序在 @autorelease 块的末尾崩溃。看起来自动释放池正在尝试释放已手动 CFRleased 的 CFData/NSData 对象。
我可以使用删除 CFRelease,但我并不想更改现有代码。我真正想要的是将返回的 CFDataRef 的所有权转移给调用者。
阅读有关 ARC 的资料后,我认为在演员阵容中使用 __bridge_retained 会有所帮助。但是因为这不是用 ARC 构建的,所以编译器会给我一个警告/错误:
error: '__bridge_retained' casts have no effect when not using ARC [-Werror,-Warc-bridge-casts-disallowed-in-nonarc]
使用 CFDataCreateCopy 创建 NSData 的副本确实有效,但似乎很浪费。
在这里处理这个问题的正确方法是什么?有没有另一种方法来投射这个?
【问题讨论】:
CFRelease((CFDataRef)[NSData dataWithContentsOfURL:url]);
是一个错误,故事结束。你不会释放你不拥有的东西。虽然您应该拥有它,因为该函数称为..Create..
。如果不更改其中的一些代码,就无法解决这个问题。
是的,我明白了。我的问题是如何获得该对象的所有权?我认为 __bridge_retained 会这样做,但显然不是在不使用 ARC 时。
@Droopycom 你倒退了。您试图在非 ARC 代码中使用 __bridge_retained
,但您只能在 ARC 代码中使用它。
【参考方案1】:
您有一个名为CFDataCreateFromResource
的方法。这个名字意味着被返回的对象正在将所有权传递给调用者。但是,您对此方法的实现将返回一个您不拥有的自动释放对象。这违反了合同。
一种解决方案是改变:
NSData *binData = [NSData dataWithContentsOfURL:url];
到:
NSData *binData = [[NSData alloc] initWithContentsOfURL:url];
这会更改binData
的所有权,因此它与方法的合同相匹配。
通过这个简单的更改,您在test
中对CFRelease
的调用将正常工作。
【讨论】:
啊,我原以为使用 [NSData alloc] 创建的 NSData 与创建 NSData 的任何其他方式具有相同的所有权语义。 这不是特定于NSData
。使用alloc/init...
创建的任何对象都会返回您拥有的对象。按照惯例,使用便捷方法(例如dataWith...
)创建的任何对象都会返回一个不属于您的自动释放对象。以上是关于NSData/CFData 桥接 @autorelease 但不是 ARC的主要内容,如果未能解决你的问题,请参考以下文章