SecItemCopyMatching仍然泄漏在osx下ARC [英] SecItemCopyMatching still leak on osx under ARC

查看:3476
本文介绍了SecItemCopyMatching仍然泄漏在osx下ARC的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现内存泄漏在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); <-------这里是一个泄漏符号


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); <-------这里是一个泄漏符号


if != 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)

解决方案


  • 您从其名称中使用复制的CF样式函数接收到keychain对象。因此,它有一个+1引用计数,你有责任明确地释放它,当你完成使用它。它不会被你的示例代码释放,所以它是泄漏的。

  • 在第一个解决方案中,你传递 attributeQuery (局部变量)和一个简单的 __ bridge cast,这不是一个好主意; 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 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);
}];
}



我发现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);
(身份的id值){
SecCertificateRef cfcertificate;
SecIdentityRef cfidentity =(SecIdentityRef)CFBridgingRetain(value);
status = SecIdentityCopyCertificate(cfidentity,& cfcertificate);
if(status == errSecSuccess){
// ...
CFRelease(cfcertificate);
}
}
}


i found memory leak on SecItemCopyMatching. After investigate on SF i was found solution:

__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)
        {

or another solution:

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)

But both of them still do leak for me.

Any another ideas

解决方案

  • 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);
        }
    }
 }

这篇关于SecItemCopyMatching仍然泄漏在osx下ARC的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆