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

Gab*_*Cas 0 memory memory-leaks objective-c ios

打开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;
}
Run Code Online (Sandbox Code Playgroud)

正如您在此处看到的,它会产生内存泄漏: 在此输入图像描述

或者,这个其他函数(基本相同)会产生其他类型的内存泄漏:

- (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;
}
Run Code Online (Sandbox Code Playgroud)

产生这些内存泄漏: 在此输入图像描述

什么导致这些内存泄漏?该代码适用于打开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;
Run Code Online (Sandbox Code Playgroud)

}

Rob*_*ier 8

你在泄漏items.你创建它但从不发布它.你不应该首先创建它.通过引用SecPKCS12Import返回items.你没有把它传给现有的; 它通过一个.这应该是:

CFArrayRef items = NULL;
Run Code Online (Sandbox Code Playgroud)

你还需要调用CFRetain()identityApp来保持它(因为它目前仅由数组保留.因此,你需要调用你的方法,当你完成它时你copyClientCertificate2需要CFRelease()它的结果.

附注:除非通过引用返回结果,否则永远不要在方法前加上"get".这就是ObjC中的"获取"意味着什么.例如:

- (BOOL)getName:(NSString **)name;
Run Code Online (Sandbox Code Playgroud)

你在这里有另一个非常危险的代码:

NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath]; // (1)
CFDataRef inPKCS12Data = (__bridge CFDataRef)PKCS12Data;              // (2)
...
OSStatus securityError = SecPKCS12Import(inPKCS12Data, options, &items); // (3)
Run Code Online (Sandbox Code Playgroud)

ARC可PKCS12Data在第(2)行后立即释放.如果你在发布模式下构建它,我会期望它崩溃.这里正确的解决方案是摆脱inPKCS12Data,并__bridge在第(3)行执行演员表.


编辑:

如果您正在进行此类工作,了解如何管理Core Foundation对象非常重要.首先,研究下面链接的创建规则.然后,调用该方法的目的copy…是指示调用者负责调用CFRelease返回的对象.它看起来像这样:

SecIdentityRef identity = [self copyClientCertificate2];
... Do what you need to do with identity ....
CFRelease(identity);
Run Code Online (Sandbox Code Playgroud)

由于您从未使用selfcopyClientCertificate2,因此将它作为一个函数可能更有意义,因此它看起来像其他Core Foundation函数:

SecIdentityRef identity = MYSecIdentityCopyClientCertificate();
... Do what you need to do with identity ....
CFRelease(identity);
Run Code Online (Sandbox Code Playgroud)

请注意Copy函数名称中的单词.