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的主要内容,如果未能解决你的问题,请参考以下文章

数组与指针

Automatic Reference Counting

Linux 系统排错

简述内存管理

Auto reloading enabled

UINavigationBar 自定义标题被背景隐藏