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.
Related
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.
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);
I've made an iPhone app using ARC that accesses every entry in the address book, and then every address for every person. The data is stored in CFArrays, which are toll-free bridged to NSArrays. The code is below.
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef arrayRef = ABAddressBookCopyArrayOfAllPeople(addressBook);
NSArray *peopleArray =[(__bridge NSArray *) arrayRef copy];
CFRelease(arrayRef);
arrayRef = nil;
for(id personId in peopleArray)
{
ABRecordRef person = (__bridge ABRecordRef) personId;
//process other attributes of the address book
ABMultiValueRef multi = ABRecordCopyValue(person, kABPersonAddressProperty);
CFArrayRef addressRef = ABMultiValueCopyArrayOfAllValues(multi);
NSArray *addressArray = [(__bridge NSArray *) addressRef copy];
for(NSDictionary *address in addressArray)
{
//process the addresses
}
CFRelease(addressRef);
addressRef = nil;
}
From what I've researched on the internet and in Apple's Memory Management guides, this looks like the proper way to do it. The problem is when I got to run the code, it halts on "CFRelease(addressRef)", highlighted green with text "Thread 1" (not sure what this error means). I've also tried putting the CFRelease before the for loop, but the same issue occurs.
If I remove the CFRelease, it compiles, but there is a memory leak at the creation of addressArray. Does anyone know how to solve this problem? I can't seem to figure it out using ARC.
Instead of NSArray *peopleArray =[(__bridge NSArray *) arrayRef copy]; CFRelease(arrayRef);, use NSArray *peopleArray = CFBridgingRelease(arrayRef). This transfers ownership of the object to ARC.
Whenever you see "Copy" in a method name you should use (__bridge_transfer <ObjectType> *)
Then ARC will be responsible for releasing the object.
So your code will look like:
NSArray *peopleArray = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
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.)