使用ARC将CFErrorRef转换为NSError(或相反)

时间:2013-12-06 15:04:14

标签: objective-c cocoa automatic-ref-counting

我曾经像这样将NSError强制转换为CFErrorRef并在SMJobBless中使用它

NSError *error
BOOL removed = SMJobRemove(kSMDomainSystemLaunchd,
                               (CFStringRef) daemonBundleID,
                               auth,
                               true,
                               (CFErrorRef*) &error);
if (!removed) {
        NSLog(@"Failed to remove existing PacketTool");
        [NSApp presentError: error];
    }

由于我在ARC中遇到错误,“ARC禁止使用指向'CFErrorRef'的Obj-C指针的间接指针”,我改变并决定做相反的事情

CFErrorRef *cfError = nil;
BOOL blessed = SMJobBless(kSMDomainSystemLaunchd, (__bridge CFStringRef)daemonBundleID,
                          auth,
                          cfError);
if (!blessed) {
    NSError *error = (__bridge NSError *)cfError;
    NSLog(@"Failed to bless PacketTool: %@", error);
    [NSApp presentError: error];
    return FALSE;
}

现在我有一个“不兼容的类型''CFErrorRef'到NSError *”,带有__bridge cast

我该怎么办?

更新:感谢Greg,现在是正确的代码:

CFErrorRef cfError = nil;
BOOL blessed = SMJobBless(kSMDomainSystemLaunchd,
                          (__bridge CFStringRef) daemonBundleID,
                          auth,
                          &cfError);
if (!blessed) {
    NSError *error = (__bridge NSError *)cfError;
    NSLog(@"Failed to bless PacketTool: %@", error);
    [NSApp presentError: error];
    return FALSE;
}

2 个答案:

答案 0 :(得分:20)

当你声明cfError时你不应该使用指针*,你应该使用:

CFErrorRef cfError = nil;
NSError *error = (__bridge NSError *)cfError;

另一方面,它的工作原理如下:

NSError *error = nil;
CFErrorRef ref = (__bridge CFErrorRef) error;

希望得到这个帮助。

答案 1 :(得分:10)

2013年12月7日16:05,汤姆补充道:

  

更新:感谢Greg,现在是正确的代码:

CFErrorRef cfError = nil;
BOOL blessed = SMJobBless(kSMDomainSystemLaunchd,
                          (__bridge CFStringRef) daemonBundleID,
                          auth,
                          &cfError);
if (!blessed) {
    NSError *error = (__bridge NSError *)cfError;
    NSLog(@"Failed to bless PacketTool: %@", error);
    [NSApp presentError: error];
    return FALSE;
}

我知道这篇文章已经有2年了,但它错了,我不希望其他程序员复制错误的代码。 此代码泄漏内存,因为CFError永远不会被释放!

CoreFoundation没有自动内存管理,使用ARC时也没有。 ARC仅适用于Obj-C对象。并且CoreFoundation不知道自动释放自动释放池,因此CoreFoundation对象(CFStringRefCFNumberRefCFErrorRef等)你从CoreFoundation函数获得永远不会自动释放。它们要么根本不需要释放,要么由它来释放它们。如果出现错误(CFErrorRef *),则由您自行决定。

另见https://stackoverflow.com/a/8628267/15809

代码的第一部分是正确的:

CFErrorRef cfError = nil;
BOOL blessed = SMJobBless(
    kSMDomainSystemLaunchd,
    (__bridge CFStringRef)daemonBundleID,
    auth, &cfError
);

但是你需要了解桥梁铸造。最简单的形式或桥接铸造只是__bridge,这个演员告诉ARC“不要做任何事”。如果你这样做

NSError * error = (__bridge NSError *)cfError;

你告诉ARC:“将cfError投放到error ,但是在投放后不管理错误的记忆,这不关你的事。

如果你这样做,你仍然有责任释放CFErrorRef!这意味着一旦你完成cfError error(“和”因为两者都指向同一个对象,如果它被销毁,两个指针都变得无效),你必须这样做:

CFRelease(cfError);

否则你是在泄漏记忆!

或者你可以告诉ARC为你管理内存,但是你需要一个不同的演员阵容。如果你像那样投射

NSError * error = (__bridge_transfer NSError *)cfError;

你告诉ARC:“将cfError投射到error ,然后由你来管理error的记忆。

现在您不需要发布任何内容,因为只要error超出范围,ARC就会为您发布。由于errorcfError实际上是同一个对象,因此发布error也会释放cfError,所以现在您不需要发布任何内容。顾名思义,此转换将对象“转移”到ARC。完成后,您不能再使用cfError了,因为您无法确定ARC何时会释放error,并且一旦确定,cfError就是无效的指针使用它可以轻松崩溃整个应用程序。

如果你向另一个方向投掷也是如此。如果你这样做

NSError * error = ...;
CFErrorRef cfError = (__bridge CFErrorRef)error;

ARC仍将管理error的内存,这很危险,如上所述,当ARC决定它可以销毁error时,cfError也将无效。如果你只在当前范围内使用cfError,这是没关系的,但是如果你的CFErrorRef需要生存而不管ARC做什么,那么你就做了这个演员:

NSError * error = ...;
CFErrorRef cfError = (__bridge_retained CFErrorRef)error;

这告诉ARC:“保留error一次,然后将error投射到cfError ,并且不要平衡此初始保留。< /强>“

因此,即使error超出范围,ARC也不会释放它,因为对象的保留计数器不会变为0,因为该转换,它仍然至少为1。现在由您来处理内存管理,这意味着一旦完成cfError,您必须释放它:

CFRelease(cfError);