I've got a doubt about using "CFStringRef". I've seen several examples of managing iPhone contacts and in most of them they use:
...
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
CFStringRef emailRef = ABMultiValueCopyValueAtIndex(emails, 0);
...
NSString *email = (NSString *)emailRef;
I don't know why CFStringRef is used instead of using casting:
...
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
NSString *email = (NSString *)ABMultiValueCopyValueAtIndex(emails, 0);
...
Is it conventional to use CFStringRef? Is it correct to use direct casting?
CFString is “toll-free bridged” with its Cocoa Foundation counterpart,
NSString. This means that the Core Foundation type is interchangeable
in function or method calls with the bridged Foundation object.
Therefore, in a method where you see an NSString * parameter, you can
pass in a CFStringRef, and in a function where you see a CFStringRef
parameter, you can pass in an NSString instance. This also applies to
concrete subclasses of NSString.
Both are same.
There is no difference, the first example just uses an extra temporary variable. Performing a straight cast is perfectly OK, although you should pay attention to the memory management aspect. It sounds like ABMultiValueCopyValueAtIndex returns a retained object although the documentation isn't clear.
Related
I am using the following line of code...
NSString *clientFirstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
The 'analyse' feature on Xcode is saying that this giving rise to a potential memory leak. I am not releasing clientFirstName at all as I have neither alloc or retain'd it.
However, I am conscious that ABRecordCopyValue may not be returning an object as say a command like [NSMutableArray arrayWithArray:someArray] would which might mean I am indeed creating a new object that I control and must release.
Keen to hear thoughts...
Any sort of copy returns an object with a retainCount of 1, so you need to release it via CFRelease().
See the doc:
You are responsible for releasing this object.
Refer to this link. It has same as yours:
NSString potential leak
Refer to answer of KennyTM:
ABMultiValueCopyValueAtIndex is a "Copy" function, which follows
the "Create
Rule".
You need to call CFRelease to release it after finish using it.
NSString *contactEmail = (NSString *)ABMultiValueCopyValueAtIndex(emailInfo, 0);
...
if (contactEmail != nil)
CFRelease((CFTypeRef) contactEmail);
Release your object clientFirstName using CFRelease.
Add the code below:
if(clientFirstName != nil)
{
CFRelease((CFTypeRef) clientFirstName);
}
NOTE: Do not forget to check if clientFirstName is not nil. It is better to have a practice of checking that object is not nil before executing any function on it as it saves us from potential crashes though not in this case, but in many cases
EDIT:
Also as #Dondragmer says in one of the comments below:
I think even
[clientFirstName release];
should solve the problem.
Let me know if you need more help.
Hope this helps you.
I don't fully understand how ObjectiveC reference counting works, but a small update in code fixed the Analyze warning:
Before:
NSString *firstName = (__bridge NSString*) ABRecordCopyValue(person, kABPersonFirstNameProperty);
After
NSString *firstName = (__bridge_transfer NSString*) ABRecordCopyValue(person, kABPersonFirstNameProperty);
After trying to print retainCount of object I get 2147483647. Why do I get such a result? It should be 1, shouldn't?
NSString *myStr = [NSString new];
NSUInteger retCount = [myStr retainCount];
NSLog(#"refCount = %u", retCount);
2011-09-08 17:59:18.759 Test[51972:207] refCount = 2147483647
I use XCode Version 4.1. Tried compilers GCC 4.2 and LLVM GCC 4.2 - the same result.
Garbage Collection option was set to unsupported.
NSString is somewhat special when it comes to memory management. String literals (something like #"foo") are effectively singletons, every string with the same content is the same object because it can't be modified anyway. As [NSString new] always creates an empty string that cannot be modified, you'll always get the same instance which cannot be released (thus the high retainCount).
Try this snippet:
NSString *string1 = [NSString new];
NSString *string2 = [NSString new];
NSLog(#"Memory address of string1: %p", string1);
NSLog(#"Memory address of string2: %p", string2);
You'll see that both strings have the same memory address and are therefore the same object.
This doesn't directly answer your question, but retainCount is not really all that useful and should not be used for testing. See this SO post for details.
When to use -retainCount?
While NSString's are an odd case (there are others in the framework) you might also run across this in other clases - it's one of the ways of creating a singleton object.
A singleton only exists once in the app and it's pretty important that it never gets released! Therefore, it will overwrite some methods of NSObject including (but not limited to):
- (id)release {
// Ignore the release!
return self;
}
- (NSUInteger)retainCount {
// We are never going to be released
return UINT_MAX;
}
This object can never be released and tells the framework that it's a singleton by having a ludicrously high retain count.
Checkout this link for more information about singleton objects.
I've seen this a couple of times regarding NSStrings, the retainCount returns the maximum count instead of the actual one when you try to look at retainCounts of strings in this manner.
Try this;
NSString *myStr = [[NSString alloc] init];
NSUInteger retCount = [myStr retainCount];
NSLog(#"refCount = %u", retCount);
Edit: Restored NSUInteger
In my app, static analyser points a leak in the following code:
ABMultiValueRef phone = (NSString *)ABRecordCopyValue(person, kABPersonPhoneProperty);
NSString *mobilephone= (NSString*)ABMultiValueCopyValueAtIndex (phone,0);
similarly whenever i use this function ABRecordCopyValue it points a leak
I tried to release it by [phone release]; method, however I am getting a compiler warning "invalid receiver type 'abmultivalueref'". What is the proper way to release this ?
It looks like you are confusing the NS data types with the CF data types. The address book methods typically return core foundation (CF) objects. These objects are toll-free bridged, which means they can be used interchangeably with NS types.
When using core foundation objects, any method with 'copy' in its name will return an object that you later need to release using CFRelease. Only if you cast it to its NS equivalent can you use - release.
So your code could be written as either of the following:
ABMultiValueRef phone = ABRecordCopyValue(person, kABPersonPhoneProperty);
NSString *mobilephone = (NSString *)ABMultiValueCopyValueAtIndex(phone, 0);
// other code
[mobilephone release];
or
ABMultiValueRef phone = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFStringRef mobilephone = ABMultiValueCopyValueAtIndex(phone, 0);
// other code
CFRelease(mobilephone);
Have you tried with CFRelease(phone); ?
Because ABMultiValueCopyValueAtIndex is not a NSString, it's a CFStringRef
Using __bridge_transfer ensures that ARC will release the object for you.
Using __bridge means you must release the returned object manually.
I am reading records from the AddressBook using the Apple provided API.
I am still getting my head around memory management and so CFStrings are confusing me at the moment.
This is how I am getting the properties:
//Get Basic properties
NSString* firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString* lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
NSNumber* record = [NSNumber numberWithInt:ABRecordGetRecordID(person)];
//Build Full Name
NSString* fullName=[self fullNameWith:firstName and:lastName];
//Get Phone number and Label
ABMultiValueRef phone = ABRecordCopyValue(person, property);
//Turn identifier into index
CFIndex index = ABMultiValueGetIndexForIdentifier(phone, identifier);
//Get the value and the label using the index
NSString *value =(NSString *)ABMultiValueCopyValueAtIndex(phone, index);
CFStringRef label = ABMultiValueCopyLabelAtIndex(phone, index);
//Get the localized value of hte label
NSString * localizedLabel = (NSString *)ABAddressBookCopyLocalizedLabel(label);
After that I use the values, the only thing is that I dont know if I should release them or not.
I would appreciate an answer that also helped me understand memory management better or that points me to the right direction.
Thank you!
The rule of thumb for Core Foundation is that any functions that include Copy or Create in their name will return an object that you are responsible for releasing. Apple's Memory Management Guide for Core Foundation explains this in a bit more detail.
When I build and analyze my project on XCode, I obtain a 'warning' on the following line:
NSString *contactEmail = (NSString *)ABMultiValueCopyValueAtIndex(emailInfo, 0);
The message is: Potential leak on object allocated on line ... and stored into contactEmail.
Is there any error on that line?
UPDATE
I get the same 'warning' with this line of code:
ABMultiValueRef emailInfo = ABRecordCopyValue(person, kABPersonEmailProperty);
But here, I can't do this:
[emailInfo release];
I'm developing for iPhone.
ABMultiValueCopyValueAtIndex is a "Copy" function, which follows the "Create Rule". You need to call CFRelease to release it after finish using it.
NSString *contactEmail = (NSString *)ABMultiValueCopyValueAtIndex(emailInfo, 0);
...
if (contactEmail != nil)
CFRelease((CFTypeRef) contactEmail);
The cast is somewhat pointless.
The line might leak, unless you release or autorelease it somewhere.
Edit: For brevity:
NSString *contactEmail = [(NSString *)ABMultiValueCopyValueAtIndex(emailInfo, 0) autorelease];
(The cast might still be pointless, I'm unsure as to how the compiler would handle trying sending a message directly to a CFTypeRef.)