打开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;
}
产生这些内存泄漏:
导致这些内存泄漏的是什么?该代码适用于打开p12文件,但我需要修复内存泄漏。任何帮助表示赞赏。
谢谢!
修改
根据建议的更改,我仍然会收到内存泄漏:
- (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;
}
答案 0 :(得分:7)
你正在泄漏items
。你创建它但从不发布它。你不应该首先创建它。 SecPKCS12Import
通过引用返回items
。你没有把它传给现有的;它通过你一个。这应该是:
CFArrayRef items = NULL;
您还需要在CFRetain()
上调用identityApp
来保留它(因为它目前仅由数组保留。因此,您的方法需要调用copyClientCertificate2
并且您完成后需要CFRelease()
结果。
附注:除非通过引用返回结果,否则不要在方法前加上“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)行执行__bridge
演员。
编辑:
如果您正在进行此类工作,了解如何管理Core Foundation对象非常重要。首先,研究下面链接的创建规则。然后,调用方法copy…
的目的是指示调用者负责在返回的对象上调用CFRelease
。它看起来像这样:
SecIdentityRef identity = [self copyClientCertificate2];
... Do what you need to do with identity ....
CFRelease(identity);
由于您从未在self
中使用copyClientCertificate2
,因此将其设为函数可能更有意义,因此它看起来像其他Core Foundation函数:
SecIdentityRef identity = MYSecIdentityCopyClientCertificate();
... Do what you need to do with identity ....
CFRelease(identity);
请注意函数名称中的单词Copy
。