1

My app asks users to select one of their contacts and then one of the contact’s addresses. I want to generate a “display name” to use to refer to this address: for example, if the user selects one of John Smith’s addresses, the display name is “John Smith” and my UI will refer to his address as “John Smith’s address”. The algorithm for extracting this name from the address record is as follows:

  1. If the contact is a business, use the business’s name.
  2. If there is a first name and a last name, use “firstname lastname”.
  3. If there is a first name use the first name.
  4. If there is a last name use the last name.
  5. Use the string “Selected Contact”.

I have all this logic implemented. The problem is that I sometimes see crashes (KERN_INVALID_ADDRESS) on one of the two marked lines. My app uses ARC, and I don’t have a lot of experience with Core Foundation, so I assume I’m doing the memory management or bridging incorrectly. Can anyone tell me what I’m doing wrong, and how to fix the crashes? The relevant two methods are as follows:

- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
       shouldContinueAfterSelectingPerson:(ABRecordRef) person
                                 property:(ABPropertyID) property
                               identifier:(ABMultiValueIdentifier) identifier
{
    [self dismissViewControllerAnimated:YES completion:NULL];

    CFTypeRef address = ABRecordCopyValue(person, property);
    NSArray *addressArray = (__bridge_transfer NSArray *)ABMultiValueCopyArrayOfAllValues(address);
    CFRelease(address);
    NSDictionary *addressDict = [addressArray objectAtIndex:0];

    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder geocodeAddressDictionary:addressDict completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error || !placemarks || [placemarks count] == 0) {
            // tell the user that there was an error
        } else {
            NSString *name = contactName(person);
            NSString *addressName = [NSString stringWithFormat:@"%@’s address", name];
            // use `addressName` to refer to this address to the user
        }
    }];

    return NO;
}

NSString* contactName(ABRecordRef person)
{
    NSString *name;

    // some crashes occur on this line:
    CFNumberRef contactType = ABRecordCopyValue(person, kABPersonKindProperty);

    if (contactType == kABPersonKindOrganization)
        name = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonOrganizationProperty);

    if (!name || [name length] == 0 || contactType == kABPersonKindPerson) {
        // other crashes occur on this line:
        NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
        NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);

        if (firstName && [firstName length] > 0 && lastName && [lastName length] > 0)
            name = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
        else if (firstName && [firstName length] > 0)
            name = firstName;
        else if (lastName && [lastName length] > 0)
            name = lastName;

        if (!name || [name length] == 0)
            name = @"Selected Contact";
    }

    CFRelease(contactType);

    return name;
}
4

1 回答 1

2

假设您的调用dismissViewControllerAnimated:completion:正在解除人员选取器控制器,并且您的person对象由该控制器保留,那么您person可能在完成处理之前就被释放了。只需将dismissViewControllerAnimated:completion:调用移至该方法的末尾即可解决问题。如果此处理在关闭视图控制器之前导致太多延迟,请将person值复制到变量 ARC 将为您保留,关闭视图控制器,然后处理人员。

于 2013-05-20T13:03:06.277 回答