Ios 使用ARC的安全框架内存泄漏

Ios 使用ARC的安全框架内存泄漏,ios,objective-c,memory,memory-leaks,Ios,Objective C,Memory,Memory Leaks,我在打开ASIHTTPRequest的p12证书时遇到内存泄漏。这是我获取证书的代码: - (SecIdentityRef)getClientCertificate { SecIdentityRef identityApp = nil; NSString *thePath = [[NSBundle mainBundle] pathForResource:@"myCert" ofType:@"p12"]; NSData *PKCS12Data = [[NSData allo

我在打开ASIHTTPRequest的p12证书时遇到内存泄漏。这是我获取证书的代码:

- (SecIdentityRef)getClientCertificate {
    SecIdentityRef identityApp = nil;
    NSString *thePath = [[NSBundle mainBundle] pathForResource:@"myCert" ofType:@"p12"];
    NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;
    CFStringRef password = CFSTR("myPassword");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
    CFRelease(options);
    CFRelease(password);
    if (securityError == errSecSuccess) {
        NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);        
    } else {
        NSLog(@"Error opening Certificate.");
    }
    return identityApp;
}
正如您在这里看到的,它会产生内存泄漏:

或者,此其他功能(基本相同)会产生其他类型的内存泄漏:

- (SecIdentityRef)getClientCertificate2 {
    SecIdentityRef identityApp = nil;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *thePath = [documentsDirectory stringByAppendingPathComponent:@"myothercert.p12"];

    NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
    NSLog(@"PKCS12Data length is %i", [PKCS12Data length]);
    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;

    CFStringRef password = CFSTR("randomgenerated");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
     CFRelease(options);
    //CFRelease(password);
    if (securityError == errSecSuccess) {
        NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
    } else {
        NSLog(@"Error opening Certificate.");
    }
    return identityApp;
}
- (SecIdentityRef)copyClientCertificate2 {
SecIdentityRef identityApp = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *thePath = [documentsDirectory stringByAppendingPathComponent:@"cert.p12"];

NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
NSLog(@"PKCS12Data length is %i", [PKCS12Data length]);


CFStringRef password = CFSTR("randomgenerated");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = NULL;
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)PKCS12Data, options, &items);
 CFRelease(options);
//CFRelease(password);
if (securityError == errSecSuccess) {
    NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
    CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
    identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
} else {
    NSLog(@"Error opening Certificate.");
}
CFRetain(identityApp);
return identityApp;
产生这些内存泄漏的原因:

是什么导致了这些内存泄漏?代码可以很好地打开p12文件,但我需要修复内存泄漏。谢谢你的帮助

谢谢

编辑

通过建议的更改,我仍然会收到内存泄漏:

- (SecIdentityRef)getClientCertificate2 {
    SecIdentityRef identityApp = nil;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *thePath = [documentsDirectory stringByAppendingPathComponent:@"myothercert.p12"];

    NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
    NSLog(@"PKCS12Data length is %i", [PKCS12Data length]);
    CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;

    CFStringRef password = CFSTR("randomgenerated");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items);
     CFRelease(options);
    //CFRelease(password);
    if (securityError == errSecSuccess) {
        NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
        CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
        identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
    } else {
        NSLog(@"Error opening Certificate.");
    }
    return identityApp;
}
- (SecIdentityRef)copyClientCertificate2 {
SecIdentityRef identityApp = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *thePath = [documentsDirectory stringByAppendingPathComponent:@"cert.p12"];

NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath];
NSLog(@"PKCS12Data length is %i", [PKCS12Data length]);


CFStringRef password = CFSTR("randomgenerated");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = NULL;
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)PKCS12Data, options, &items);
 CFRelease(options);
//CFRelease(password);
if (securityError == errSecSuccess) {
    NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
    CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
    identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
} else {
    NSLog(@"Error opening Certificate.");
}
CFRetain(identityApp);
return identityApp;

}

您正在泄漏
项目
。你创造了它,但从不释放它。你不应该一开始就创造它
secpkcs12导入
通过引用返回
。你没有通过一个现有的;它从你身边经过。这应该是:

CFArrayRef items = NULL;
您还需要在
identityApp
上调用
CFRetain()
来保留它(因为它当前仅由数组保留。因此,您的方法需要调用
copyClientCertificate2
,并且在使用完它后需要
CFRelease()
它的结果

旁注:除非方法通过引用返回结果,否则不要在方法前面加上“get”。这就是“get”在ObjC中的含义。例如:

- (BOOL)getName:(NSString **)name;
这里还有一段非常危险的代码:

NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath]; // (1)
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;              // (2)
...
OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items); // (3)
ARC可以在第(2)行之后立即释放
PKCS12Data
。如果在释放模式下构建,我预计它会崩溃。正确的解决方案是删除
inPKCS12Data
,并在第(3)行执行
u桥
转换


编辑:

<> P>这是非常重要的,如果你正在做这样的工作,你应该了解如何管理核心基础对象。首先,研究下面链接的创建规则。然后,调用方法<代码>拷贝…<代码>的点是指示调用方负责在返回的对象上调用<代码> cFrRys<代码>。

SecIdentityRef identity = [self copyClientCertificate2];
... Do what you need to do with identity ....
CFRelease(identity);
因为在 CopyclitLogiCudit2中,你从不使用<代码>“自我/代码>”,使它成为一个函数更有意义,因此它看起来像其他核心基础函数:

SecIdentityRef identity = MYSecIdentityCopyClientCertificate();
... Do what you need to do with identity ....
CFRelease(identity);

请注意函数名中的单词
Copy

谢谢,我会尝试一下,让您知道我已经实现了您的建议(查看我的编辑)但是仍然存在内存泄漏。我做错了什么?感谢您的帮助!何时释放返回的
SecIdentityRef
?是否将其保留在其他位置?是否仍然泄漏相同的对象(是
SecIdentityRef
?a
CFArrayRef
)我正在按照您的建议对SecIdentityRef执行CFRetain。这是在我将其返回到函数之前完成的。我如何从整个列表中确定泄漏的对象?是那些在名称前有下划线的对象,如NSCFArray吗?是的,但您何时释放SecIdentityRef?您必须平衡保留和释放。这是CoRealField.它遵循创建规则:在泄露列表中列出的对象指示对象不能从任何已知指针到达(因此它是“丢失的”)堆栈跟踪告诉您内存分配的位置;这并不能告诉您您的错误在哪里。您必须确保您正在遵循所有核心基础对象上的创建规则。