Code works fine in Xcode Simulator, but when I tested it on real device, app hangs with error:
EXC_BAD_ACCESS, (code 1, address 0x0)
ABMultiValueRef multi = ABRecordCopyValue(person, kABPersonEmailProperty);
NSString* contactEmail = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(multi, 0);
CFRelease(multi);
Upd.: Works on iPhone also, if contact has email field filled. If not, it crashes.
OK, checking for zero prevents from crashing.
if (ABMultiValueGetCount(phones) > 0) {
contactPhone = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(multi, 0);
}
Related
I'm trying to get name and last name from Contacts and store the values in a couple of NSString:
CFStringRef cfName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
self.stName = (NSString *)cfName;
CFRelease(cfName);
CFStringRef cfLastname = ABRecordCopyValue(person, kABPersonLastNameProperty);
self.stLastname = (NSString *)cfLastname;
CFRelease(cfLastname);
The problem is, if firstname or lastname are empty in contacts, when I release the CFStringRef the app crash with EXC_BAD_ACCESS (zombie?).
If I don't release the CFStringRef I have a leak.
Any suggestion is appreciated.
Max
Yes, CFRelease will crash (halt) if you pass it a NULL argument.
This was allready mentioned by sbooth here.
CFRelease looks like this:
void CFRelease(CFTypeRef cf) {
if (NULL == cf) HALT; //this line will get you
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
if (CF_IS_COLLECTABLE(cf)) {
if (CFTYPE_IS_OBJC(cf)) {
// release the GC-visible reference.
auto_zone_release(auto_zone(), (void*)cf);
} else {
// special-case CF objects for better performance.
_CFRelease(cf);
}
return;
}
#endif
source
Therefor it's your responcibility to check if you're passing it a NULL or not.
You were correct in your (deleted) answer. Code should look like:
CFStringRef cfName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
self.stName = (NSString *)cfName;
if (self.stName) CFRelease(cfName);
CFStringRef cfLastname = ABRecordCopyValue(person, kABPersonLastNameProperty);
self.stLastname = (NSString *)cfLastname;
if (self.stLastname) CFRelease(cfLastname);
You still might get compiler warnings but this code should work fine.
I need to fetch record from address book. I'm using ABPeoplePickerNavigationController to do so. While fetching record I'm putting a check condition to check record exist or not.
For example to check there is first name I'm using the following code
NSString *fname;
CFTypeRef fnameProperty = ABRecordCopyValue(person, kABPersonFirstNameProperty);
if (ABRecordCopyValue(person, kABPersonFirstNameProperty)) {
fname = (__bridge NSString*)fnameProperty;
}
else{
fname = #"";
}
The code works perfectly for kABPersonFirstNameProperty.
But when I'm using following piece of code to check email property
ABMultiValueRef emailProperty = ABRecordCopyValue(person, kABPersonEmailProperty);
NSLog(#"%#",emailProperty);
NSString *email;
if (ABRecordCopyValue(person, kABPersonEmailProperty)) {
email = (__bridge NSString*)ABMultiValueCopyValueAtIndex(emailProperty, 0);
}
else {
email = #"";
}
My app crashes if kABPersonEmailProperty is nil(there is no email property stored for the person)
Can somebody explain me why is this happening?
Thanks
Here's is my working version, see if this works.
//Email
NSString* email = nil;
ABMultiValueRef emailNum = ABRecordCopyValue(person,
kABPersonEmailProperty);
if (ABMultiValueGetCount(emailNum) > 0) {
email = (__bridge_transfer NSString*)
ABMultiValueCopyValueAtIndex(emailNum, 0);
} else {
email = #"";
}
I'm having a bit of an issue with using the address book to get the names of the contacts from the device into my own contacts view within my application.
The code I have works fine on the emulator but I when tested on an iPhone 4 it will crash, the application seems to work fine if there are two or less contacts but 3 or more and the application crashes.
Here is the code I am using to get the names of contacts into an array.
ABAddressBookRef addressBook;
bool wantToSaveChanges = YES;
bool didSave;
CFErrorRef error = NULL;
addressBook = ABAddressBookCreate();
listOfContacts = [[NSMutableArray alloc]init];
int i;
int len = (int) ABAddressBookGetPersonCount(addressBook);
for(i = 1; i< (len+1); i++){
ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID) i);
NSString* name = (NSString *)ABRecordCopyCompositeName(person);
ABMultiValueRef number = (NSString *)ABRecordCopyValue(person,kABPersonPhoneProperty);
NSString *mobileNum = (NSString *)ABMultiValueCopyValueAtIndex(number, 0 );
NSLog(#"Name = %#", name);
NSLog(#"Number = %#", mobileNum);
[listOfContacts addObject:name];
[name release];
[mobileNum release];
}
if(ABAddressBookHasUnsavedChanges(addressBook)){
if(wantToSaveChanges){
didSave = ABAddressBookSave(addressBook, &error);
if(!didSave){
//Error
}
}
else{
ABAddressBookRevert(addressBook);
}
}
When it crashes this is the line that gets highlighted in Xcode:
NSString* name = (NSString *)ABRecordCopyCompositeName(person);
And the error states:
Thread 1: Program received signal: "EXC_BAD_ACCESS"
Can anyone see what the problem might be? I dont understand why it would work on the emulator but not on the device? And also why it works for up to two contacts but not 3 or more??
Just a guess:
ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID) i);
This line looks fishy for me. I doubt that the record IDs are numbered from 1 to whatever. Especially if you have deleted an entry.
This would explain why it works on the simulator, I guess you just added some test contacts and never deleted one.
Here is how I solved it:
(Note how I make sure to call only active records, also I created a customized Contact class)
This code takes care of edge case like: email/phone doesn't exist, or exists more than once...
+(NSArray *)getAddressBook{
ABAddressBookRef addressBook;
bool wantToSaveChanges = YES;
bool didSave;
CFErrorRef error = NULL;
addressBook = ABAddressBookCreate();
NSMutableArray *listOfContacts = [[NSMutableArray alloc]init];
CFArrayRef array=ABAddressBookCopyArrayOfAllPeople(addressBook);
int len=CFArrayGetCount(array);
for (int i = 0; i<len; i++){
ABRecordRef person = CFArrayGetValueAtIndex(array, i);
if (ABRecordGetRecordType(person)==kABPersonType){
NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
ABMultiValueRef emails = (ABMultiValueRef)ABRecordCopyValue(person,kABPersonEmailProperty);
ABMultiValueRef numbers = (ABMultiValueRef)ABRecordCopyValue(person,kABPersonPhoneProperty);
int sumEmails=ABMultiValueGetCount(emails);
int sumNumbers=ABMultiValueGetCount(numbers);
for (int j=0; j<(sumNumbers>sumEmails?sumNumbers:sumEmails); j++) {
ACL_AB_Contact *contact=[[ACL_AB_Contact alloc]initWithFirstName:firstName LastName:lastName];
if (j<sumEmails){
contact.emailAddress=(NSString *)ABMultiValueCopyValueAtIndex(emails,j);
}
if (j<sumNumbers){
contact.phoneNumber=(NSString *)ABMultiValueCopyValueAtIndex(numbers,j);
}
[contact logContact];
[listOfContacts addObject:contact];
[contact release];
}
}
}
if(ABAddressBookHasUnsavedChanges(addressBook)){
if(wantToSaveChanges){
didSave = ABAddressBookSave(addressBook, &error);
if(!didSave){
//Error
}
}
else{
ABAddressBookRevert(addressBook);
}
}
return [listOfContacts autorelease];
}
The record IDs are dynamic. It means that if you add 2 contacts and then remove the first, you will have only a contact with id "2". So I wouln't use a for statement to get through the contacts. Follow the Address Book Programming Guide
I am trying to get all the phone number from the Phone Book of iPhone. But my app crashes as soon as I try to fetch the number using kABPersonPhoneProperty. The code snippet is here
ABAddressBookRef addressBook = ABAddressBookCreate();
NSMutableArray *allPeople = (NSMutableArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
int nPeople = ABAddressBookGetPersonCount(addressBook);
CFRelease(addressBook);
for(int i=0; i < nPeople; i++ ){
ABRecordRef person = [allPeople objectAtIndex:i];
NSString *name = #"";
CFTypeRef fName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
CFTypeRef lName = ABRecordCopyValue(person, kABPersonLastNameProperty);
ABMultiValueRef multi = ABRecordCopyValue(person , kABPersonPhoneProperty);
NSString *number = (NSString *)ABMultiValueCopyValueAtIndex(multi, 0);
if ([number length] > 0) {
if(fName != NULL)
{
if (lName != NULL) {
name = [[NSString stringWithFormat:#"%#", fName] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
else {
name = [[NSString stringWithFormat:#"%#", fName] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
NSLog(#"Number==>%# ",number);
}
}
if (fName != NULL) {
CFRelease(fName);
}
if (lName != NULL) {
CFRelease(lName);
}
if (multi != NULL) {
CFRelease(multi);
}
}
[allPeople release];
I am unable to figure out the error in this. I am also releasing everything even then it is not running smoothly.
I am even getting potential memory leak when running build and analyze for the code part
ABMultiValueRef multi = ABRecordCopyValue(person , kABPersonPhoneProperty);
NSLog(#"Number===>%d" , ABMultiValueGetCount(multi));
Please help me out to come.
Any kind of help would be highly appreciated.
Thanks in advance
Thanks for the response. I have figured out the solution fot the problem here. I did not release number variable thinking that the reference was passed as reference but it is copied to the returned location, hence need to release number after the usage.
thanks again for all the responses!!
I'm stumped.
I'm trying to get a list of all the email address a person has.
I'm using the ABPeoplePickerNavigationController to select the person, which all seems fine. I'm setting my
ABRecordRef personDealingWith;
from the person argument to
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
and everything seems fine up till this point.
The first time the following code executes, all is well.
When subsequently run, I can get issues. First, the code:
// following line seems to make the difference (issue 1)
// NSLog(#"%d", ABMultiValueGetCount(ABRecordCopyValue(personDealingWith, kABPersonEmailProperty)));
// construct array of emails
ABMultiValueRef multi = ABRecordCopyValue(personDealingWith, kABPersonEmailProperty);
CFIndex emailCount = ABMultiValueGetCount(multi);
if (emailCount > 0) {
// collect all emails in array
for (CFIndex i = 0; i < emailCount; i++) {
CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i);
[emailArray addObject:(NSString *)emailRef];
CFRelease(emailRef);
}
}
// following line also matters (issue 2)
CFRelease(multi);
If compiled as written, the are no errors or static analysis problems. This crashes with a
*** -[Not A Type retain]: message sent to deallocated instance 0x4e9dc60
error.
But wait, there's more! I can fix it in either of two ways.
Firstly, I can uncomment the NSLog at the top of the function. I get a leak from the NSLog's ABRecordCopyValue every time through, but the code seems to run fine.
Also, I can comment out the
CFRelease(multi);
at the end, which does exactly the same thing. Static compilation errors, but running code.
So without a leak, this function crashes. To prevent a crash, I need to haemorrhage memory. Neither is a great solution.
Can anyone point out what's going on?
It turned out that I wasn't storing the ABRecordRef personDealingWith var correctly. I'm still not sure how to do that properly, but instead of having the functionality in another routine (performed later), I'm now doing the grunt-work in the delegate method, and using the derived results at my leisure. The new (working) routine:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
// as soon as they select someone, return
personDealingWithFullName = (NSString *)ABRecordCopyCompositeName(person);
personDealingWithFirstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
// construct array of emails
[personDealingWithEmails removeAllObjects];
ABMutableMultiValueRef multi = ABRecordCopyValue(person, kABPersonEmailProperty);
if (ABMultiValueGetCount(multi) > 0) {
// collect all emails in array
for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) {
CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i);
[personDealingWithEmails addObject:(NSString *)emailRef];
CFRelease(emailRef);
}
}
CFRelease(multi);
return NO;
}
I ran into a similar problem. The problem might lie at how you set your
ABRecordRef personDealingWith;
It seems that you can't just do:
ABRecordRef personDealingWith = person;
Because personDealingWith remains null. Instead what I did is:
ABRecordID personID = ABRecordGetRecordID(person);
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
personDealingWith = ABAddressBookGetPersonWithRecordID(addressBook, personID);