【问题标题】:SecItemCopyMatching still leak on osx under ARCSecItemCopyMatching 仍然在 ARC 下的 osx 上泄漏
【发布时间】:2013-05-27 20:33:00
【问题描述】:

我在 SecItemCopyMatching 上发现内存泄漏。在对 SF 进行调查后,我找到了解决方案:

__block NSString *certificateName = nil;
SecKeychainRef keychain;
SecKeychainCopyDefault(&keychain);
NSMutableDictionary *attributeQuery = [NSMutableDictionary dictionary];
[attributeQuery setObject: (id) kSecClassIdentity forKey:(__bridge_transfer id) kSecClass];
[attributeQuery setObject: (id) kCFBooleanTrue forKey:(__bridge_transfer id) kSecReturnRef];
[attributeQuery setObject: (id) kSecMatchLimitAll forKey:(__bridge_transfer id) kSecMatchLimit];
CFTypeRef attrResult = NULL;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) attributeQuery,(CFTypeRef *) &attrResult);<------- here is a leak according Instruments


if (status != errSecItemNotFound) {
    NSArray *attributeResult = (__bridge_transfer NSArray *)attrResult;
    [attributeResult enumerateObjectsUsingBlock:^(id identityFromArray, NSUInteger idx, BOOL *stop) {
        OSStatus status;
        SecCertificateRef cert = NULL;
        status = SecIdentityCopyCertificate((__bridge SecIdentityRef)identityFromArray, &cert);
        if (!status)
        {

或其他解决方案:

NSMutableDictionary *attributeQuery = [NSMutableDictionary dictionary];
[attributeQuery setObject: (id) kSecClassIdentity forKey:(__bridge_transfer id) kSecClass];
[attributeQuery setObject: (id) kCFBooleanTrue forKey:(__bridge_transfer id) kSecReturnRef];
[attributeQuery setObject: (id) kSecMatchLimitAll forKey:(__bridge_transfer id) kSecMatchLimit];
CFTypeRef attrResult = NULL;
CFDictionaryRef cfquery = (__bridge_retained CFDictionaryRef)attributeQuery;
OSStatus status = SecItemCopyMatching(cfquery,(CFTypeRef *) &attrResult);<------- here is a leak according Instruments


if (status != errSecItemNotFound) {
    NSArray *attributeResult = (__bridge_transfer NSArray *)attrResult;
    [attributeResult enumerateObjectsUsingBlock:^(id identityFromArray, NSUInteger idx, BOOL *stop) {
        OSStatus status;
        SecCertificateRef cert = NULL;
        status = SecIdentityCopyCertificate((__bridge SecIdentityRef)identityFromArray, &cert);
        if (!status)
        {
            char *nameBuf = NULL;
            CFStringRef nameRef = NULL;
            OSStatus statusNew = SecCertificateInferLabel(cert, &nameRef);
            .....
CFRelease(cfquery)

但是他们两个仍然对我泄漏。

还有什么想法

【问题讨论】:

    标签: iphone macos cocoa automatic-ref-counting


    【解决方案1】:
    • 您从名称中带有Copy 的CF 样式函数接收钥匙串对象。因此,它有一个 +1 的引用计数,并且您有责任在使用完它后明确地释放它。您的示例代码永远不会释放它,因此它会泄漏。您发布的代码中从未使用过钥匙串对象,因此可以完全消除它。
    • 在第一个解决方案中,您通过简单的__bridge 强制转换传递attributeQuery(局部变量),这不是一个好主意; ARC 可能会过早地从您下方释放它。您应该使用 __bridge_retained(或 CFBridgingRetain)将其转换为具有 +1 保留计数的 CF 国家/地区(并在稍后明确发布)。
    • 在第二个解决方案中,您使用__bridge_retained,但您没有发布结果,这解释了泄漏。
    • 如果调用成功,SecItemCopyMatching 的返回值为零。你不应该只和errSecItemNotFound比较;查询失败可能有许多其他原因。

    更新代码:

    NSMutableDictionary *attributeQuery = [NSMutableDictionary dictionary];
    [attributeQuery setObject:(id)kSecClassIdentity forKey:(__bridge id)kSecClass];
    [attributeQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnRef];
    [attributeQuery setObject:(id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
    CFTypeRef cfresult = NULL;
    CFDictionaryRef cfquery = (CFDictionaryRef)CFBridgingRetain(attributeQuery);
    OSStatus status = SecItemCopyMatching(cfquery, &cfresult);
    CFRelease(cfquery);
    
    if (status == errSecSuccess) {
        NSArray *attributeResult = CFBridgingRelease(cfresult);
        [attributeResult enumerateObjectsUsingBlock:^(id value, NSUInteger idx, BOOL *stop) {
            OSStatus status;
            SecCertificateRef cert = NULL;
            SecIdentityRef identity = CFBridgingRetain(value);
            status = SecIdentityCopyCertificate(identity, &cert);
            CFRelease(identity);
            if (!status)
            {
               ...
               CFRelease(cert);
            }];
     }
    

    我发现 Core Foundation/Cocoa 桥接转换有点难以阅读,所以我个人认为跳过 Cocoa 级别并直接在 CF 级别创建查询字典更简洁,如下所示:

    CFMutableDictionaryRef cfquery = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(cfquery, kSecClass, kSecClassIdentity);
    CFDictionarySetValue(cfquery, kSecReturnRef, kCFBoolenTrue);
    CFDictionarySetValue(cfquery, kSecMatchLimit, kSecMatchLimitAll);
    
    CFArrayRef cfidentities = NULL;
    OSStatus status = SecItemCopyMatching((CFDictionaryRef)cfquery, (CFTypeRef *)&cfidentities);
    CFRelease(cfquery);
    
    if (status == errSecSuccess) {
        NSArray *identities = CFBridgingRelease(cfidentities);
        for (id value in identities) {
            SecCertificateRef cfcertificate;
            SecIdentityRef cfidentity = (SecIdentityRef)CFBridgingRetain(value);
            status = SecIdentityCopyCertificate(cfidentity, &cfcertificate);
            if (status == errSecSuccess) {
                // ...
                CFRelease(cfcertificate);
            }
        }
     }
    

    【讨论】:

    • tnx for u answer: 1. 版本在 CFRelease(attrResult) 上崩溃;但没有它和 CFRelease(cert);内存泄漏消失了。 tnx 完美解释
    • 不要删除CFRelease(cert)SecIdentityCopyCertificate 的名称中有 Copy,因此它返回的结果带有 +1 引用计数,您应该自己释放。
    猜你喜欢
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 2015-06-20
    • 2012-12-09
    • 2011-10-23
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    相关资源
    最近更新 更多