In my app, I have to retrieve certain properties of the users contacts. For example, I need to retrieve a contact's first name, last name, middle name, nickname, organization, job title, department, birthday, email, etc. I have some methods to retrieve these properties, and only a couple work, even though they all are very similar. Here is my code for one method that is working (first name) and one that is not (job title):
+(NSString *)fetchFirstnameForPersonID: (NSUInteger)identifier{
NSString *firstName;
ABRecordRef currentPerson = (__bridge ABRecordRef)[[PSAddressBook arrayOfContacts] objectAtIndex:identifier];
//If the first name property exists
if ((__bridge_transfer NSString *)ABRecordCopyValue(currentPerson, kABPersonFirstNameProperty) != NULL){
firstName = (__bridge_transfer NSString *)ABRecordCopyValue(currentPerson, kABPersonFirstNameProperty);
}
//If the first name property does not exist
else{
firstName = @"NULL";
}
return firstName;
}
+(NSString *)fetchJobTitleForPersonID: (NSUInteger)identifier{
NSString *jobTitle;
ABRecordRef currentPerson = (__bridge ABRecordRef)[[PSAddressBook arrayOfContacts] objectAtIndex:identifier];
if ((__bridge_transfer NSString *)ABRecordCopyValue(currentPerson, kABPersonJobTitleProperty) != NULL){
jobTitle = (__bridge_transfer NSString *)ABRecordCopyValue(currentPerson, kABPersonJobTitleProperty);
}
else{
jobTitle = @"NULL";
}
return jobTitle;
}
arrayOfContacts
is a class method defined like this:
+(NSArray *)arrayOfContacts{
//Creates an ABAddressBookRef instance containing the data from the address book database
ABAddressBookRef addressBook = ABAddressBookCreate();
//Creates an NSArray from the CFArrayRef using toll-free bridging
NSArray *arrayOfPeople = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
CFRelease(addressBook);
return arrayOfPeople;
}
These methods are defined in a model class called "PSPropertyFetcher". In my root view controller, I put some NSLog statements in viewDidLoad to see if the property fetcher methods are working properly. Here is my code for the tests:
NSLog(@"Property Fetchers Test\n");
for(NSUInteger i = 0; i <= ([PSAddressBook contactsCount]-1); i++){
NSLog(@"First Name: %@", [PSPropertyFetcher fetchFirstnameForPersonID:i]);
NSLog(@"Middle Name: %@", [PSPropertyFetcher fetchMiddlenameForPersonID:i]);
NSLog(@"Last Name: %@", [PSPropertyFetcher fetchLastnameForPersonID:i]);
NSLog(@"Organization: %@", [PSPropertyFetcher fetchOrganizationForPersonID:i]);
NSLog(@"Department: %@", [PSPropertyFetcher fetchDepartmentForPersonID:i]);
NSLog(@"Job Title: %@\n\n", [PSPropertyFetcher fetchJobTitleForPersonID:i]);
}
This works only partly; this is the output:
2012-06-27 10:37:30.094 Guess Who![80103:f803] Property Fetchers Test
2012-06-27 10:37:30.108 Guess Who![80103:f803] First Name: Jod
2012-06-27 10:37:30.114 Guess Who![80103:f803] Middle Name: Bob
2012-06-27 10:37:30.118 Guess Who![80103:f803] Last Name: Satson
2012-06-27 10:37:30.122 Guess Who![80103:f803] Organization: Johnson and Johnson
2012-06-27 10:37:30.125 Guess Who![80103:f803] Department: NULL
2012-06-27 10:37:30.128 Guess Who![80103:f803] Job Title: NULL
2012-06-27 10:37:30.136 Guess Who![80103:f803] First Name: Shemairan
2012-06-27 10:37:30.166 Guess Who![80103:f803] Middle Name: Daitran
2012-06-27 10:37:30.179 Guess Who![80103:f803] Last Name: Catairan
2012-06-27 10:37:30.184 Guess Who![80103:f803] Organization: Shmairo and Co.
2012-06-27 10:37:30.188 Guess Who![80103:f803] Department: NULL
2012-06-27 10:37:30.193 Guess Who![80103:f803] Job Title: NULL
2012-06-27 10:37:30.202 Guess Who![80103:f803] First Name: Alex
2012-06-27 10:37:30.207 Guess Who![80103:f803] Middle Name: John
2012-06-27 10:37:30.213 Guess Who![80103:f803] Last Name: Corn
2012-06-27 10:37:30.219 Guess Who![80103:f803] Organization: Apple
2012-06-27 10:37:30.225 Guess Who![80103:f803] Department: NULL
2012-06-27 10:37:30.230 Guess Who![80103:f803] Job Title: NULL
In the iOS Simulator Contacts application, I made sure to fill out every field for each contact, but for some reason, the "Department" and "Job Title" fields are not printing correctly.
Basically, I'm wondering what's wrong with my "Job Title" and "Department" fetcher methods.
Thanks in advance!
While it doesn't seem likely on such a small sampling of an address book, you do have a memory leak here that could be blowing things out.
Assuming you are using arrayOfContacts
to determine [self contactsCount]
, you will be leaking 2 AddressBook items every time through every loop in those class methods.
In this small sample, you will have only leaked 48 AddressBooks by the time you start contactsWithJobTitleProperty. But if you've pulled this from a larger example where you're repeatedly attempting to determine these values on a much larger AddressBook, you could have hundreds of dangling AddressBook objects hanging around.
Add CFRelease as in the following snippet, and see if it helps.
+(NSArray *)arrayOfContacts{
//Creates an ABAddressBookRef instance containing the data from the address book database
ABAddressBookRef addressBook = ABAddressBookCreate();
//Creates an NSArray from the CFArrayRef using toll-free bridging
NSArray *arrayOfPeople = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
CFRelease(addressBook);
return arrayOfPeople;
}
(fwiw, a separate thing that will crash your program is that you are using NSUInteger as your loop counter type, but you test against a value that could potentially be -1 ... and since 0 is the floor for NSUInteger, if your address book has no contacts in it, this will fail. but that is not why this crash is occurring.)
It looks to me like you may be accessing a NULL value, have you tried testing if it's NULL before performing the bridge transfer?
This might not solve your problem, but I would a couple of adjustments if I were you:
+(NSUInteger)contactsWithJobTitleProperty{
NSUInteger contactNumber = 0;
// don't call arrayOfContacts in the loop --
// when you do this you are creating a new address book
// each time the loop executes
NSArray *contacts = [self arrayOfContacts];
for(NSUInteger increment = 0; increment <= (contacts.count-1); increment++){
ABRecordRef currentPerson = (__bridge ABRecordRef)[contacts objectAtIndex:increment];
NSString *valueTest = (__bridge_transfer NSString *)ABRecordCopyValue(currentPerson, kABPersonJobTitleProperty);
if(valueTest != NULL) {
contactNumber++;
}
}
return contactNumber;
}
Also, @john.k.doe mentioned, make sure to use CFRelease(addressBook);
in your arrayOfContacts
method.
Just encountered the same issue. See Jokinryou Tsui's answer here.
Basically, if there is a situation where you have different sources of contacts available, and they are not all local (the default), you could end up with a mismatch of counts of how many contacts you're pulling, and try to access an ABRecordRef at an index that doesn't exist. The ABRecordRef will have a value (so a NULL check alone won't do it), but that value will be garbage and you'll get the BAD ACCESS when trying to copy any of its values.
Hope this helps.
来源:https://stackoverflow.com/questions/11174892/abrecordcopyvalue-exc-bad-access-error