Reputation: 1037
I'm trying to implement a feature in my app which requires me to present all the contacts in the user's phone that has Email in it.
I successfully retrieved the relevant contacts from the Address Book
framework, and kept the info in an array. Each contact is wrapped in wrapper contain 2 properties: fullName
and email
, which are both NSString
s
Now I've populated the array of contacts in a table in my view controller and when the table is static without any user touching or scrolling everything is great and life are awesome! But when I scroll down to reveal some more contacts, I get a crash!
The crash
No log is dumped to the console. I have green line (same as when you breakpoint with the debugger) on one of the lines in tableView:cellForRowAtIndexPath:
says the following: Thread 1: EXC_BAD_ACCESS (code=1, address=0x10d9eb4e)
My investigations
I have a strong reference to the contact array. I've checked the content of that array. and noticed that all the items that are visible to the user (meaning, around 11 items) are absolutely valid. All the field are correct with the expected data (full name and email).
The rest of the items in the array on the other hand, are seemed corrupted or something. I can see the the items are of the kind NRContactInfo
, which has 2 properties: _fullName
and _email
- but even though the type of those 2 properties is NSString
, the content isn't a string, but a memory address or something (for instance: 0x114e2b3b0).
I've noticed that the crash happens the first time in tableView:cellForRowAtIndexPath:
where I making a call to one of the NRContactInfo
properties, so if I'm deleting all the calls to NRContactInfo's properties from the tableView:cellForRowAtIndexPath:
and just use dummy info, then everything is good.
So I think it has to do with losing the info retrieved from AddressBook. but I can't figure out why or how to fix it.
Some code of mine for the helpers (edited)
UPDATE
I removed some unconnected/unnecessary parts of the code, for clarity. I've added the implementation of NRContactInfo class.
Here is a code snippet of the tableView:cellForRowAtIndexPath:
method which crashes as I explained above.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = @"tableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
NRContactInfo *contact = contacts[indexPath.row];
cell.textLabel.text = contact.fullName; // <------ CRASHES HERE!
cell.detailTextLabel.text = contact.email;
return cell;
}
Thanks in advance for every piece of information that will help me fix this issue!
UPDATE
I'm posting here the code that retrieves the contact information from the AddressBook and creates the NRContactsInfo objects, as Volker Voecking pointed out in his comment:
- (void)getPersonsOutOfAddressBook
{
//In case of an error we create an error object.
CFErrorRef error = NULL;
//We get a reference to the user's address book.
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if ([self checkForPermmissionsToAccessAddressBook:addressBook]){
if (addressBook != nil) {
NSLog(@"Succesful.");
//we copy all contacts from the addressbook in an array
NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
//We loop through the contacts in the address book. For each contact, we create a NRContactInfo Object.
NSUInteger i = 0; for (i = 0; i < [allContacts count]; i++)
{
ABRecordRef contactPerson = (__bridge ABRecordRef)allContacts[i];
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonLastNameProperty);
NSString *fullName = @"";
if (firstName) {
fullName = [fullName stringByAppendingString:firstName];
}
if (lastName) {
fullName = [fullName stringByAppendingString:@" "];
fullName = [fullName stringByAppendingString:lastName];
}
NSString *email;
//The email property of the addressbook is assigned to a multivalue reference
ABMultiValueRef emails = ABRecordCopyValue(contactPerson, kABPersonEmailProperty);
if (ABMultiValueGetCount(emails)>0){
NSUInteger j = 0;
for (j = 0; j < ABMultiValueGetCount(emails); j++) {
email = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(emails, j);
NRContactInfo *contact =[NRContactInfo initWithFullName:fullName Email:email];
if (contact.isValid)
[_contacts addObject:contact];
}
}
}
CFRelease(addressBook);
} else {
NSLog(@"Error reading Address Book: %@", error);
}
}
}
- (BOOL)checkForPermmissionsToAccessAddressBook:(ABAddressBookRef) addressBook
{
__block BOOL userDidGrantAddressBookAccess = NO;
if ( ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined ||
ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized ) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error){
userDidGrantAddressBookAccess = granted;
});
}
else {
if ( ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusDenied ||
ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusRestricted ) {
// Display an error.
}
}
return userDidGrantAddressBookAccess;
}
Implementation of NRContactInfo:
+ (NRContactInfo *)initWithFullName:(NSString *)fullName Email:(NSString *)email
{
NRContactInfo *contact = [NRContactInfo new];
contact.fullName = [NSString stringWithFormat:@"%@", fullName];
contact.email = [NSString stringWithFormat:@"%@", email];
return contact;
}
- (BOOL)isValid
{
if (_fullName &&_email)
return YES;
return NO;
}
Upvotes: 0
Views: 113
Reputation: 1037
While editing my answer to be more accurate and to contain more info regarding my problem, i've noticed the source of the problem:
In my NRContactInfo
class I had 2 properties which were declared as follows:
@property (nonatomic, assign) NSString *fullName;
@property (nonatomic, assign) NSString *email;
This is the reason for the weird crashes. Apparently even though I was sure I had a valid references to those properties, I didn't!
This is what fixed my problem:
@property (nonatomic, retain) NSString *fullName;
@property (nonatomic, retain) NSString *email;
Thanks for all the helpers, and for all the rest beware of the COPY & PASTE sin :D
Upvotes: 1