35

我有点困惑。在我读过的任何地方,都建议在使用 ARC 时,您仍然需要发布有意义的核心基础对象,ARC 不管理它们。但是,我有一个方法使用了一些我使用CFRelease过的 CF 方法/对象,但这会导致应用程序崩溃。取消注释我CFRelease的 s 解决了这个问题,但我假设我有内存泄漏?

有人可以解释一下哪些东西需要发布,哪些不需要,或者这段代码有什么问题吗?

+ (NSString *) fileExtensionForMimeType:(NSString *)type
{
    CFStringRef mimeType = (__bridge CFStringRef)type;
    CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
    CFStringRef extension = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);

    NSString *ext = (__bridge NSString *)extension;

    // CFRelease(mimeType);
    // CFRelease(uti);
    // CFRelease(extension);

    return ext;
}

CFRelease如前所述,三个注释掉的调用解决了这个问题,但我知道这是错误的。我应该做什么?

4

3 回答 3

39

你不能释放mimeType,因为你不拥有它。您没有与__bridge演员一起转让所有权。

你应该发布uti,因为你已经创建了它。

您还应该发布extension,因为您也创建了它,但这可能会导致ext. 相反,将所有权转让给ext.

我建议如下:

+ (NSString *) fileExtensionForMimeType:(NSString *)type {
    CFStringRef mimeType = (__bridge CFStringRef)type;
    CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
    CFStringRef extension = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);

    NSString *ext = (__bridge_transfer NSString *)extension;

    // CFRelease(mimeType); // not owned
    if (uti) CFRelease(uti);
    // CFRelease(extension); // ownership was transferred

    return ext;
}
于 2012-12-28T04:00:19.307 回答
16

查看WWDC 2012 - Modern Objective-C,它概述了 Core Foundation 对象和 ARC 的新指南。那个视频大约是 37:35。简而言之,Core Foundation 函数CopyCreate名称创建了一个对象,该对象已将所有权转移到您的应用程序,并且您的应用程序负责释放它。

无论如何,如果所有权已通过 Core Foundation 方法以名称CopyCreate名称转移,您可以在完成后手动释放CFRelease它,或者更简单,您可以将所有权转移给 ARC 并让它处理它. 从历史上看,为了将所有权转移给 ARC,我们使用__bridge_transfer,但他们现在推荐CFBridgingRelease(尽管后者只是前者的宏)。而且,显然,如果您有一些通过其他机制检索到的核心基础对象,而不是名称CopyCreate名称中的函数,那么您既不应该CFRelease将其所有权转移给 ARC,也不应该将其所有权转移给 ARC。

通过说明,此方法可以完成您想要的:

+ (NSString *) fileExtensionForMimeType:(NSString *)type {

    NSString *uti = CFBridgingRelease(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,
                                                                            (__bridge CFStringRef)type,
                                                                            NULL));

    return CFBridgingRelease(UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)uti,
                                                             kUTTagClassFilenameExtension));
}
于 2012-12-28T04:23:11.960 回答
0

一般来说,我认为您应该尝试注释第一行 CFRelease(mimeType),并取消注释后面的两行:CFRelease(uti) 和 CFRelease(extension)。你投了一个免费的桥来输入 NSString 并且 ARC 将处理发布。但是 uti 和扩展名是作为 CFString 创建/复制的。ARC 不知道如何处理它们(记住 ARC 是 NSObject 的编译器功能),因此您需要 CF 释放它们。

于 2012-12-28T04:03:06.737 回答