我在SecItemCopyMatching上发现了内存泄漏。在对SF i进行调查后,找到了解决方案:
__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)
但他们两人仍为我泄漏。
任何其他想法
答案 0 :(得分:8)
Copy
。因此它具有+1引用计数,您有责任在使用它时明确释放它。它从未被您的示例代码发布,因此它正在泄漏。钥匙串对象从未在您发布的代码中使用,因此可以完全取消。attributeQuery
演员传递__bridge
(一个局部变量),这不是一个好主意; 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);
for (id value in identities) {
SecCertificateRef cfcertificate;
SecIdentityRef cfidentity = (SecIdentityRef)CFBridgingRetain(value);
status = SecIdentityCopyCertificate(cfidentity, &cfcertificate);
if (status == errSecSuccess) {
// ...
CFRelease(cfcertificate);
}
}
}