Reputation: 1382
I have a method that returns an NSArray of a custom object called "Credential" that has two properties: an NSString and a CFDataRef.
As you have noticed the object has two types of properties, a NS Objective-C property and a Core-Foundation property.
The object is initialized at every interation loop as it fills the NSArray like this:
cred = [[Credential alloc] init];
cred.cn = [NSString stringWithString:(__bridge NSString *)(summary)];
cred.serialNumber = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);
When I run the analyzer I'm getting the message:
Object leaked: allocated object is not referenced later in this
execution path and has a retain count of +1
I'm assuming this warning happens because I'm initializing a CF object and returning from the method without releasing it, yet the new responsible for releasing the object should be the code calling the method.
Where should I call the CFRelease of the CFDataRef property of the Credential class?
EDIT:
I'm using ARC so I leave to him the responsibility of releasing the NSString (cred.cn). About the CFDataRef (cred.serialNumber), though, I'm not releasing it, since I'll need it later from another class and part of the code. Then, I'm not sure how to manage it. Does ARC release it when the object "Credential" is disposed? If not, can I overwrite the dealloc method of Credential to do the CFRelease of serialNumber there?
Here's the full method that initializes and returns the NSArray of Credential objects:
- (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;
}
Upvotes: 2
Views: 1999
Reputation: 108101
With the line
cred.serialNumber = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);
CFDataCreateCopy
creates an object with a +1 retain count, which you're not releasing anywhere after. That's why the analyzer is warning you.
Replacing that line with the following code should fix it
CFDataRef sn = CFDataCreateCopy(kCFAllocatorDefault, serialNumber);
cred.serialNumber = sn;
CFRelease(sn);
The key is that you're messing with ownerships, therefore confusing the analyzer.
You're expected to release whatever object you created with New, Copy or Retain functions within the current scope, so you really shoud release the object you created with CFDataCreateCopy
.
If then the Credential
instance needs to retain the value you're assign to serialNumber
, he should be responsible for that, not the caller.
In order to do so, just declare the serialNumber
property of Credential
as strong
or copy
and let ARC do its magic.
EDIT
Since from the comments it appears that the serialNumber
property has a CFDataRef
type, you can still have to Credential
object to retain it, by turning it in a retainable object pointer, like follows
@property (nonatomic, strong) __attribute__((NSObject)) CFDataRef serialNumber;
The NSObject
attribute will make the compiler to treat it as an object, memory management wise. This is well explained in the clang docs.
Upvotes: 1
Reputation: 385590
As others have pointed out, your creating two copies of the serial number but only releasing one.
You can convert a CF object to its corresponding NS object and take care of releasing the CF object using the macro CFBridgingRelease
. The release happens at the end of the statement, after ARC has retained the object if it needed to.
NSString *summary = CFBridgingRelease(SecCertificateCopySubjectSummary(certificate));
NSData *serialNumber = CFBridgingRelease(CFDictionaryGetValue(result, kSecAttrSerialNumber));
cred = [[Credential alloc] init];
cred.cn = [summary copy];
cred.serialNumber = [serialNumber copy];
The basic rule is that you can use CFBridgingRelease
instead of CFRelease
. It balances the retain from the CF Create or Copy function, and it returns an Objective-C object reference that ARC will take care of.
Upvotes: 1
Reputation: 119031
If the string in created using a CF function which means you own it, you should transfer the ownership to ARC. Currently you're just bridging the reference to ARC won't take ownership.
For the data, you need to override dealloc
and call CFRelease
on the data.
The analyzer isn't perfect. Some things it finds difficult so it errs on the side of caution and tells you there might be a problem.
To be sure, particularly with leaks and memory management, you should use Instruments
to check what's happening.
Upvotes: 2