使用 CFRelease 时发生内存泄漏

Memoryleak when using CFRelease

我有仪器显示内存泄漏的方法:

-(BOOL)checkIfGroupExistWithName:(NSString*)groupName
{
    BOOL hasGroup = NO;
    //checks to see if the group is created ad creats group for Handheld contacts

    CFErrorRef error = NULL;
    ABAddressBookRef ab = ABAddressBookCreateWithOptions(NULL, &error);

    CFIndex groupCount = ABAddressBookGetGroupCount(ab);
    CFArrayRef allGroups = ABAddressBookCopyArrayOfAllGroups(ab);

    for (int i=0; i<groupCount; i++) {

        ABRecordRef group = CFArrayGetValueAtIndex(allGroups, i);

        CFStringRef  CFcurrentGroupName = ABRecordCopyCompositeName(group);
        NSString *currentGroupName = (__bridge_transfer NSString *)CFcurrentGroupName;

        if ([currentGroupName isEqualToString:groupName]) {
            //!!! important - save groupID for later use
            groupId = ABRecordGetRecordID(group);
            hasGroup = YES;
            i = (int) groupCount;
        }
        CFRelease(CFcurrentGroupName);
        CFRelease(group);
    }


    return hasGroup;
}

如果我使用 CFRelease(ab);在 return hasGroup 之前,它崩溃了。 我不明白这里发生了什么。

静态分析器(shift+command+B or "Analyze"在 Xcode 的 "Product" 菜单上)非常擅长为您识别这些问题:

最重要的是,Create Rule 规定您必须 CFRelease 从 Core Foundation 函数返回的任何名称中带有 CopyCreate 的对象(除了那些您可以使用 CFBridgingRelease__bridge_transfer 转让所有权,因为这允许 ARC 为您清理这些)。

不用说,你不应该 CFRelease 其他任何东西(例如 group,它是由名称中没有 CopyCreate 的函数返回的,也不是 CFcurrentGroupName,您已经通过 __bridge_transfer).

转让了所有权

无论如何,你最终会得到这样的结果:

- (BOOL)checkIfGroupExistWithName:(NSString*)groupName {
    BOOL hasGroup = NO;
    //checks to see if the group is created ad creats group for Handheld contacts

    CFErrorRef error = NULL;
    ABAddressBookRef ab = ABAddressBookCreateWithOptions(NULL, &error);
    if (!ab) {
        NSLog(@"%@", CFBridgingRelease(error));
        return false;
    }

    CFIndex groupCount = ABAddressBookGetGroupCount(ab);
    CFArrayRef allGroups = ABAddressBookCopyArrayOfAllGroups(ab);

    for (int i = 0; i < groupCount; i++) {

        ABRecordRef group = CFArrayGetValueAtIndex(allGroups, i);

        NSString *currentGroupName = CFBridgingRelease(ABRecordCopyCompositeName(group));

        if ([currentGroupName isEqualToString:groupName]) {
            //!!! important - save groupID for later use
            groupId = ABRecordGetRecordID(group);
            hasGroup = true;
            break;
        }
    }

    if (allGroups) CFRelease(allGroups);
    CFRelease(ab);

    return hasGroup;
}

我怀疑您对 CFRelease(ab) 的尝试由于其他原因而崩溃(例如,abNULL 因为 ABAddressBookCreateWithOptions 失败了,也许您忽略了添加 NSContactsUsageDescription 到你的 plist;ab 在随后引用 ab 之前发布;你发布了你不应该拥有的东西;你从一些 Core Foundation 功能收到 NULL 并试图 CFRelease那个;等等)。但是上面的内容应该可以正常工作,因为它解决了上述问题。

显然,如果仅使用 iOS 9 及更高版本,您可以使用 Contacts.framework 并完全避免所有这些桥接烦恼。