use*_*317 1 iphone macos cocoa automatic-ref-counting
我在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)
{
Run Code Online (Sandbox Code Playgroud)
或其他解决方案:
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)
Run Code Online (Sandbox Code Playgroud)
但他们两个仍然为我泄漏.
任何其他想法
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);
}];
}
Run Code Online (Sandbox Code Playgroud)
我发现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);
}
}
}
Run Code Online (Sandbox Code Playgroud)