rohan-patel
rohan-patel

Reputation: 5782

ABRecordCopyValue() EXC_BAD_ACCESS Error while getting kABPersonFirstNameProperty

I am upgrading my Application written a year ago for iOS 6 to iOS 7/8 and I am getting this EXC_BAD_ACCESS error which never occurred in my old version.

In my application I am trying to fetch certain contact information like first name, last name, phone numbers, photo. Application flow is as follow:

1) Click on a button, presents address book.

2) Select any contact.

3.1) If contact has only one phone number, update the label.

3.2) If contact has multiple phone number, represent them in action sheet and whatever number user selects update that number to UILabel.

Now, if a contact has a single phone number application works fine without crash. i.e. 1-->2-->3.1 path. But if a contact has multiple phone and as soon as one contact number is selected from action sheet it crashes at this line.

 CFTypeRef firstNameCF = (__bridge CFTypeRef)(CFBridgingRelease(ABRecordCopyValue(sharedSingleton.personGlobal, kABPersonFirstNameProperty)));

Detail Code


1) Select a contact

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
  shouldContinueAfterSelectingPerson:(ABRecordRef)person {

  sharedSingleton.personGlobal = nil;
  sharedSingleton.personGlobal=person; // ====> Save a ABRecordRef object globally. 
   //^^^ Could this be a culprit? I tried to make it private variable also at first.

  [self displayAndVerifyPerson]; // No 2 below.
  [self dismissViewControllerAnimated:YES completion:^{
   }];
  return NO;
}

2) Will check how many phone nos person has got. 0/1/>1.

  If 0 show no phone no error. 

  If 1 phone update label by calling updateLabel.

  If >1 represent action sheet for user to select number. And on clickedButtonIndex call updateLabel.

-(void)displayAndVerifyPerson
{
 ABMultiValueRef phoneNumbers = ABRecordCopyValue(sharedSingleton.personGlobal,kABPersonPhoneProperty); //ABRecordRef which globally saved.
 globalContact=nil;  //NSString to store selected number. Works fine.
//self.personGlobal=person;
 NSArray *phoneNumberArray = (__bridge_transfer NSArray *)ABMultiValueCopyArrayOfAllValues(phoneNumbers);
 CFRelease(phoneNumbers);

 if (ABMultiValueGetCount(phoneNumbers) > 0){ //Check if a contact has any number

    NSLog(@" Number--> %@",phoneNumberArray); //Prints numbers correct whether no of contacts are 0/1/>1.

    if ([phoneNumberArray count]==1){  //If exactly one contact number no problem.
        globalContact = [phoneNumberArray objectAtIndex:0];
        NSLog(@"--> %@",globalContact);
        [self updateLabel];  // No 3 Below.
    }
    // We have multiple numbers so select any one.
    else{
        UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Select Number"
                                                                 delegate:self
                                                        cancelButtonTitle:nil
                                                   destructiveButtonTitle:nil
                                                        otherButtonTitles:nil];
        actionSheet.delegate=self;
        actionSheet.tag=0;
        for(int i=0;i<[phoneNumberArray count];i++){
            [actionSheet addButtonWithTitle:[phoneNumberArray objectAtIndex:i]];
        }
        [actionSheet addButtonWithTitle:@"Cancel"];
        actionSheet.destructiveButtonIndex = actionSheet.numberOfButtons - 1;
        actionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;

        UIWindow* window = [[[UIApplication sharedApplication] delegate] window];

        if ([window.subviews containsObject:self.view])
            [actionSheet showInView:self.view];
        else 
            [actionSheet showInView:window];
    }
 }
 else{ //No contact found. Display alert.

    UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error"
                                                 message:@"No contact numebr found."
                                                delegate:self
                                       cancelButtonTitle:@"OK"
                                       otherButtonTitles:nil];
    [av show];
    return;
  }
}

3) Fetch first name, Last name, Image from ABRecordRef Object.

-(void)updateLabel{

// ----------------- Get First Name From Global ABRecordRef personGlobal---------------------

    CFTypeRef firstNameCF = (__bridge CFTypeRef)(CFBridgingRelease(ABRecordCopyValue(sharedSingleton.personGlobal, kABPersonFirstNameProperty)));
    ^^^^^^^^^^^^^^^^^^^^^^
    Crashes only when `updateLabel` called from Actionsheet delegate `clickedButtonAtIndex`   

    NSString *fName = (NSString *)CFBridgingRelease(firstNameCF);
    if ([fName length]==0){
        UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Error"
                                                 message:@"Contact name not found."
                                                delegate:self
                                       cancelButtonTitle:@"OK"
                                       otherButtonTitles:nil];
        [av show];
        return;
    }
    self.lblFirstName.text = fName; //Set label with first Name.
    self.lblHomePhone.text = self.globalContact;//Set number label.
}

4) Actionsheet Delegate

-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {

  NSString *buttonTitle=[actionSheet buttonTitleAtIndex:buttonIndex];
  if(actionSheet.tag==0){
     //Printing multiple phone numbers which works and prints perfect.
     NSLog(@"Lets see what you got: ===> %@",buttonTitle); 

     if([buttonTitle isEqualToString:@"Cancel"])
        return;

     globalContact=buttonTitle;  // Save contact to NSString for later use.
     [self updateLabel];  // No. 3.
  }
}

Extra Notes:

1) Questions I looked for solution(Just 3 of many).

i) ABRecordCopyValue() EXC_BAD_ACCESS Error

ii) EXC_BAD_ACCESS when adding contacts from Addressbook?

iii) kABPersonFirstNameProperty… trowing EXC_BAD_ACCESS

2) Sample project on dropbox if someone is generous/curious enough and wants to run and check.

3) My doubts regarding this error:

Upvotes: 0

Views: 1673

Answers (2)

Daij-Djan
Daij-Djan

Reputation: 50089

you deal with CoreFoundation:

sharedSingleton.personGlobal=person;

=>

since it isn't an arc object, you have to retain it

CFRetain(person);
sharedSingleton.personGlobal=person; 

AND release it once done

- dealloc {
    CFRelease(sharedSingleton.personGlobal);    
}

Upvotes: 2

borrrden
borrrden

Reputation: 33421

Ignoring the weirdness of a lot of this code, the fundamental issue is that you are not retaining a value that you intend to use beyond the scope it is presented in. Specifically, I am referring to the person variable in section number 1. You don't retain this variable, and so it is free to be released at any time after the scope ends (which it likely does). Therefore, once you get around to calling updateLabel it is simply a dangling pointer. To fix this, you should make it a strong variable.

But wait a minute...that is only for Objective-C objects, so you need to do a little more decorating of the property. You can add __attribute__((NSObject)) to make this type behave as if it were an NSObject and subject to ARC. I can't find documentation about this anymore, but here is a reference from an old Apple Mailing List Thread

Upvotes: 1

Related Questions