Reading email address from contacts fails with weird memory issue - Solved
- by CapsicumDreams
Hi all,
I'm stumped.
I'm trying to get a list of all the email address a person has.
I'm using the ABPeoplePickerNavigationController to select the person, which all seems fine. I'm setting my
ABRecordRef personDealingWith;
from the person argument to
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
and everything seems fine up till this point.
The first time the following code executes, all is well.
When subsequently run, I can get issues. First, the code:
// following line seems to make the difference (issue 1)
// NSLog(@"%d", ABMultiValueGetCount(ABRecordCopyValue(personDealingWith, kABPersonEmailProperty)));
// construct array of emails
ABMultiValueRef multi = ABRecordCopyValue(personDealingWith, kABPersonEmailProperty);
CFIndex emailCount = ABMultiValueGetCount(multi);
if (emailCount > 0) {
// collect all emails in array
for (CFIndex i = 0; i < emailCount; i++) {
CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i);
[emailArray addObject:(NSString *)emailRef];
CFRelease(emailRef);
}
}
// following line also matters (issue 2)
CFRelease(multi);
If compiled as written, the are no errors or static analysis problems. This crashes with a
*** -[Not A Type retain]: message sent to deallocated instance 0x4e9dc60
error.
But wait, there's more! I can fix it in either of two ways.
Firstly, I can uncomment the NSLog at the top of the function. I get a leak from the NSLog's ABRecordCopyValue every time through, but the code seems to run fine.
Also, I can comment out the
CFRelease(multi);
at the end, which does exactly the same thing. Static compilation errors, but running code.
So without a leak, this function crashes. To prevent a crash, I need to haemorrhage memory. Neither is a great solution.
Can anyone point out what's going on?
Solution:
It turned out that I wasn't storing the ABRecordRef personDealingWith var correctly. I'm still not sure how to do that properly, but instead of having the functionality in another routine (performed later), I'm now doing the grunt-work in the delegate method, and using the derived results at my leisure. The new (working) routine:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
// as soon as they select someone, return
personDealingWithFullName = (NSString *)ABRecordCopyCompositeName(person);
personDealingWithFirstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
// construct array of emails
[personDealingWithEmails removeAllObjects];
ABMutableMultiValueRef multi = ABRecordCopyValue(person, kABPersonEmailProperty);
if (ABMultiValueGetCount(multi) > 0) {
// collect all emails in array
for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) {
CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i);
[personDealingWithEmails addObject:(NSString *)emailRef];
CFRelease(emailRef);
}
}
CFRelease(multi);
return NO;
}