2

我有一个方法,它返回一个名为“Credential”的自定义对象的 NSArray,它有两个属性:一个 NSString 和一个 CFDataRef。

正如您所注意到的,该对象有两种类型的属性,一个 NS Objective-C 属性和一个 Core-Foundation 属性。

该对象在每个交互循环中初始化,因为它填充 NSArray,如下所示:

cred = [[Credential alloc] init];
cred.cn = [NSString stringWithString:(__bridge NSString *)(summary)];
cred.serialNumber = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);

当我运行分析器时,我收到以下消息:

Object leaked: allocated object is not referenced later in this 
execution path and has a retain count of +1

我假设发生此警告是因为我正在初始化一个 CF 对象并从该方法返回而不释放它,但是负责释放该对象的 new 应该是调用该方法的代码。

我应该在哪里调用 Credential 类的 CFDataRef 属性的 CFRelease?

编辑:

我正在使用 ARC,所以我将释放 NSString (cred.cn) 的责任留给他。但是,关于 CFDataRef (cred.serialNumber),我没有发布它,因为稍后我会从另一个类和部分代码中需要它。然后,我不确定如何管理它。当对象“凭证”被处置时,ARC 是否会释放它?如果没有,我可以覆盖 Credential 的 dealloc 方法来执行 serialNumber 的 CFRelease 吗?

这是初始化并返回 Credential 对象的 NSArray 的完整方法:

- (NSArray *) retrieveIdentities
{
    CFArrayRef identities = NULL;
    NSMutableArray *returnIdentities = nil;
    OSStatus sanityCheck = NULL;

    const void *keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef, kSecReturnData, kSecReturnAttributes};
    const void *values[] = {kSecClassIdentity, kSecMatchLimitAll, kCFBooleanTrue, kCFBooleanTrue, kCFBooleanTrue};
    CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, sizeof(values)/sizeof(const void *), NULL, NULL);

    sanityCheck = SecItemCopyMatching(query, (CFTypeRef *)&identities);

    if (query)
        CFRelease(query);
    if (sanityCheck == errSecItemNotFound)
        return nil;
    if (sanityCheck != noErr)
        @throw [[KeychainException alloc] initWithName:@"KeychainException" reason:@"ERROR_LISTING_IDENTITIES" userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithLong: sanityCheck], @"osstatus", nil]];

    CFDictionaryRef result = NULL;
    CFStringRef summary = NULL;
    SecCertificateRef certificate = NULL;
    CFDataRef serialNumber = NULL;
    Credential *cred = nil;
    CFIndex resultCount = CFArrayGetCount(identities);

    returnIdentities = [[NSMutableArray alloc] init];

    for (CFIndex i = 0; i<resultCount; i++)
    {
        result = CFArrayGetValueAtIndex(identities,i);
        SecIdentityRef identity = (SecIdentityRef) CFDictionaryGetValue(result, kSecValueRef);

        if ((sanityCheck = SecIdentityCopyCertificate(identity, &certificate)) != noErr)
             @throw [[KeychainException alloc] initWithName:@"KeychainException" reason:@"ERROR_EXTRACTING_CERTIFICATE" userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithLong: sanityCheck], @"osstatus", nil]];

        CFTypeRef keyClass = CFDictionaryGetValue(result, kSecAttrKeyClass);
        if ([[(__bridge id)keyClass description] isEqual:(__bridge id)(kSecAttrKeyClassPrivate)])
        {
            summary = SecCertificateCopySubjectSummary(certificate);
            serialNumber = CFDataCreateCopy(NULL, CFDictionaryGetValue(result, kSecAttrSerialNumber));
            cred = [[Credential alloc] init];
            cred.cn = [NSString stringWithString:(__bridge NSString *)(summary)];
            cred.serialNumber = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);
            [returnIdentities addObject:cred];

            if (summary)
                CFRelease(summary);

            if (serialNumber)
                CFRelease(serialNumber);
        }
    }

    if (certificate)
        CFRelease(certificate);


    return returnIdentities;
}
4

3 回答 3

2

如果使用 CF 函数创建的字符串意味着您拥有它,您应该将所有权转移给 ARC。目前,您只是桥接对 ARC 的引用,不会获得所有权。

对于数据,您需要覆盖dealloc和调用CFRelease数据。


分析仪并不完美。有些事情它觉得很困难,所以它会谨慎行事,并告诉你可能有问题。

可以肯定的是,特别是在泄漏和内存管理方面,您应该使用它Instruments来检查正在发生的事情。

于 2013-06-26T14:33:23.397 回答
1

As others have pointed out, your creating two copies of the serial number but only releasing one.

You can convert a CF object to its corresponding NS object and take care of releasing the CF object using the macro CFBridgingRelease. The release happens at the end of the statement, after ARC has retained the object if it needed to.

NSString *summary = CFBridgingRelease(SecCertificateCopySubjectSummary(certificate));
NSData *serialNumber = CFBridgingRelease(CFDictionaryGetValue(result, kSecAttrSerialNumber));
cred = [[Credential alloc] init];
cred.cn = [summary copy];
cred.serialNumber = [serialNumber copy];

The basic rule is that you can use CFBridgingRelease instead of CFRelease. It balances the retain from the CF Create or Copy function, and it returns an Objective-C object reference that ARC will take care of.

于 2013-06-26T23:59:57.650 回答
1

随着线

cred.serialNumber = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);

CFDataCreateCopy创建一个保留计数为 +1 的对象,之后您不会在任何地方释放该对象。这就是分析仪警告您的原因。

用以下代码替换该行应该可以修复它

CFDataRef sn = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);
cred.serialNumber = sn;
CFRelease(sn);

关键是你搞乱了所有权,因此混淆了分析器。

您应该在当前范围释放使用 New、Copy 或 Retain 函数创建的任何对象,因此您确实应该释放使用CFDataCreateCopy.

如果然后Credential实例需要保留您分配给的值serialNumber,那么他应该对此负责,而不是调用者。为此,只需声明as or的serialNumber属性并让 ARC 发挥它的魔力。Credentialstrongcopy

编辑
由于从评论看来该serialNumber属性有一个CFDataRef类型,您仍然可以Credential通过将其转换为可保留对象指针来保留它,如下所示

@property (nonatomic, strong) __attribute__((NSObject)) CFDataRef serialNumber;

NSObject属性将使编译器将其视为一个对象,内存管理明智。这在clang 文档中有很好的解释。

于 2013-06-26T23:33:14.580 回答