SecItemCopyMatching still leak on osx under ARC

点点圈 提交于 2019-12-03 21:47:51
  • You receive the keychain object from a CF-style function with Copy in its name. Therefore it has a +1 reference count, and you have the responsibility of explicitly releasing it when you're done using it. It is never released by your sample code, so it's leaking. The keychain object is never used in the code you posted, thus it can be eliminated entirely.
  • In the first solution, you pass attributeQuery (a local variable) with a simple __bridge cast, which is not a good a idea; ARC may release it prematurely from under you. You should use __bridge_retained (or CFBridgingRetain) to convert it into CF country with a +1 retain count (and explicitly release it later).
  • In the second solution, you use __bridge_retained, but you do not release the result, which explains the leak.
  • The return value of SecItemCopyMatching is zero if the call was successful. You should not compare only against errSecItemNotFound; there can be any number of other reasons for a failed query.

Updated code:

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 attrResult = 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);
        }];
 }

I find the Core Foundation/Cocoa bridging casts a little hard to read, so I personally find it cleaner to skip the Cocoa level and create the query dictionary directly on the CF level like this:

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);
        }
    }
 }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!