我有一个方法,它返回一个名为“凭证”的自定义对象的NSArray,它有两个属性:一个NSString和一个CFDataRef。
正如您注意到的,对象有两种类型的属性,一个是NS object属性,另一个是Core-Foundation属性。
对象在每个interation循环中都被初始化,因为它填充了NSArray,如下所示:
cred = [[Credential alloc] init];
cred.cn = [NSString stringWithString:(__bridge NSString *)(summary)];
cred.serialNumber = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);当我运行分析器时,我得到的信息是:
Object leaked: allocated object is not referenced later in this
execution path and has a retain count of +1我假设发生此警告是因为我正在初始化一个CF对象,并在没有释放它的情况下从方法返回,但是新的负责释放对象的代码应该是调用该方法的代码。
我应该在哪里调用凭证类的CFRelease属性的CFDataRef属性?
编辑:
我正在使用ARC,所以我留给他发布NSString (cred.cn)的责任。但是,关于CFDataRef (cred.serialNumber),我不会发布它,因为稍后我将从另一个类和代码的一部分中需要它。那么,我不知道如何处理它。在释放对象“凭据”时ARC是否释放它?如果没有,我是否可以重写证书的dealloc方法,以便在那里执行CFRelease of serialNumber?
下面是初始化和返回凭据对象的NSArray的完整方法:
- (NSArray *) retrieveIdentities
{
CFArrayRef identities = NULL;
NSMutableArray *returnIdentities = nil;
OSStatus sanityCheck = NULL;
const void *keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef, kSecReturnData, kSecReturnAttributes};
const void *values[] = {kSecClassIdentity, kSecMatchLimitAll, kCFBooleanTrue, kCFBooleanTrue, kCFBooleanTrue};
CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, sizeof(values)/sizeof(const void *), NULL, NULL);
sanityCheck = SecItemCopyMatching(query, (CFTypeRef *)&identities);
if (query)
CFRelease(query);
if (sanityCheck == errSecItemNotFound)
return nil;
if (sanityCheck != noErr)
@throw [[KeychainException alloc] initWithName:@"KeychainException" reason:@"ERROR_LISTING_IDENTITIES" userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithLong: sanityCheck], @"osstatus", nil]];
CFDictionaryRef result = NULL;
CFStringRef summary = NULL;
SecCertificateRef certificate = NULL;
CFDataRef serialNumber = NULL;
Credential *cred = nil;
CFIndex resultCount = CFArrayGetCount(identities);
returnIdentities = [[NSMutableArray alloc] init];
for (CFIndex i = 0; i<resultCount; i++)
{
result = CFArrayGetValueAtIndex(identities,i);
SecIdentityRef identity = (SecIdentityRef) CFDictionaryGetValue(result, kSecValueRef);
if ((sanityCheck = SecIdentityCopyCertificate(identity, &certificate)) != noErr)
@throw [[KeychainException alloc] initWithName:@"KeychainException" reason:@"ERROR_EXTRACTING_CERTIFICATE" userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithLong: sanityCheck], @"osstatus", nil]];
CFTypeRef keyClass = CFDictionaryGetValue(result, kSecAttrKeyClass);
if ([[(__bridge id)keyClass description] isEqual:(__bridge id)(kSecAttrKeyClassPrivate)])
{
summary = SecCertificateCopySubjectSummary(certificate);
serialNumber = CFDataCreateCopy(NULL, CFDictionaryGetValue(result, kSecAttrSerialNumber));
cred = [[Credential alloc] init];
cred.cn = [NSString stringWithString:(__bridge NSString *)(summary)];
cred.serialNumber = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);
[returnIdentities addObject:cred];
if (summary)
CFRelease(summary);
if (serialNumber)
CFRelease(serialNumber);
}
}
if (certificate)
CFRelease(certificate);
return returnIdentities;
}发布于 2013-06-26 23:33:14
带着线
cred.serialNumber = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);CFDataCreateCopy创建一个具有+1保留计数的对象,在此之后您将不会释放它。这就是为什么分析器警告你。
用下面的代码替换该行应该会修复它
CFDataRef sn = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);
cred.serialNumber = sn;
CFRelease(sn);关键是,您正在处理所有权,因此会使分析器混淆。
您需要在当前的范围内发布用创建的任何对象,复制或保留中的函数,所以您确实应该释放使用CFDataCreateCopy创建的对象。
如果Credential实例需要保留分配给serialNumber的值,那么应该由他来负责,而不是调用者。要做到这一点,只需将Credential的Credential属性声明为strong或copy,并让ARC发挥其魔力。
编辑
由于从注释中可以看出serialNumber属性具有CFDataRef类型,所以仍然必须通过在可保留对象指针中提交Credential对象来保留它,如下所示
@property (nonatomic, strong) __attribute__((NSObject)) CFDataRef serialNumber;NSObject属性将使编译器将其作为一个对象来处理,内存管理是明智的。这在clang文档中得到了很好的解释。
发布于 2013-06-26 14:33:23
如果使用CF函数创建的字符串意味着您拥有它,则应该将所有权转移到ARC。目前,您只是连接引用ARC将不会取得所有权。
对于数据,您需要覆盖dealloc并对数据调用CFRelease。
分析器不完美。有些事情它觉得很难,所以它犯了错误,过于谨慎,并告诉你可能有问题。
当然,尤其是在泄漏和内存管理方面,您应该使用Instruments来检查正在发生的事情。
发布于 2013-06-26 23:59:57
正如其他人所指出的,您创建了两个序列号副本,但只发布了一个。
您可以将CF对象转换为对应的NS对象,并使用宏CFBridgingRelease负责释放CF对象。发布发生在语句的末尾,在ARC保留了对象之后,如果需要的话。
NSString *summary = CFBridgingRelease(SecCertificateCopySubjectSummary(certificate));
NSData *serialNumber = CFBridgingRelease(CFDictionaryGetValue(result, kSecAttrSerialNumber));
cred = [[Credential alloc] init];
cred.cn = [summary copy];
cred.serialNumber = [serialNumber copy];基本规则是您可以使用CFBridgingRelease而不是CFRelease。它平衡CF创建或复制函数中的returns,并返回ARC将处理的目标-C对象引用。
https://stackoverflow.com/questions/17319590
复制相似问题