Crash on second address book (contacts) lookup

255 views Asked by At

Update:

I believe the problem lies somewhere in how I am storing the reference to ABRecordRef. I am currently just hanging onto the value as delivered to peoplePickerNavigationController:shouldContinueAfterSelectingPerson:property:identifier: and not CFRetaining it or anything. It's unclear from the documentation if it needs to be retained.


I'm working on an iPhone app and it interfaces with the address book using the AddressBook and AddressBookUI frameworks. I'm using the ABPeoplePickerNavigationController to present a contact list to the user to choose, and am capturing the resultant ABRecordRef as an instance variable on a custom class.

This is all working fine on the first use. However, the second time I pick someone from the contacts (even a different person), my app blows up with EXC_BAD_ACCESS on a call to ABRecordCopyValue. I am logging the pointers and they are definitely different each time a contact is selected (even if the same contact twice).

I fail to understand how this reference could be deallocated. A memory leak sure, but why does it work fine the first time and not the second?

Here's the actual call it's dying on:

- (NSString*)displayName {
    return CFBridgingRelease( ABRecordCopyValue( self.contact, kABPersonFirstNameProperty ) );
}

Here's some debug output if it's helpful at all:

Printing description of self->_contact:
(ABRecordRef) _contact = 0x1f582dc0

(lldb) expr (CFTypeRef)ABRecordCopyValue(self.contact, kABPersonFirstNameProperty)
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0xd1f57cc1).
The process has been returned to the state before execution.
2

There are 2 answers

1
DogCoffee On
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    [self displayPerson:person];
    [self dismissViewControllerAnimated:YES completion:nil];

    return NO;
}

you returning NO ?

Try checking to see if the value exists maybe

ie

- (void)displayPerson:(ABRecordRef)person
{
    NSString* companyName = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonOrganizationProperty);
    NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
    NSString* display = @"";

    if (companyName) {
        display = companyName;
    } else if (name) {
        display = name;
    } else {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"No Details For Contact"
                                                            message:@"Please update contact with company and/or first name"
                                                           delegate:nil
                                                  cancelButtonTitle:@"Dismiss"
                                                  otherButtonTitles:nil];
        [alertView show];
    }
}
0
devios1 On

Turns out all I needed was to CFRetain( person ) and everything's happy go lucky again. I also added a dealloc to my class to clean up the pointer when the object goes away:

- (void)dealloc {
    CFRelease( _contact );
}

My code now runs smoothly and the static analyser is happy (not that it caught the leak anyway).