Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Interface 2, Advanced iOS
Mockup & Code Gen
($9.99)

Make your own iPhone apps
and run them live!
(free)

Pic Frame Dynamo: Photo Editing
($0.99)

Abiliator
($1.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 01-23-2012, 07:20 PM   #1 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default ABPeoplePickerNavigationController Crash

Much as I pride myself on being able to solve my own problems, I'm stumped here. Address book stuff is all in plain C, which I really don't speak very well.

Client is getting crash reports from a user or two regarding an issue that we can't replicate.

Relevant portions of crash log:


Code:
OS Version:      iPhone OS 5.0.1 (9A405)
Report Version:  104

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x00000000
Crashed Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0  CoreFoundation  0x37e7504e 0x37e72000 + 12366
1  CoreFoundation  0x37e7dcfc 0x37e72000 + 48380
2  MyFormerlyReallyAwesomeApp  0x0000e026 -[DataController makeParentInContext:fromAddressBookPerson:withFullName:] (DataController.m:3588)
3  MyFormerlyReallyAwesomeApp  0x0001742a -[AllPersonsViewController peoplePickerNavigationController:shouldContinueAfterSelectingPerson:] (AllPersonsViewController.m:1079)
DataController.m:3588 is:

Code:
if (CFStringCompare(labelRef, kABHomeLabel, 0) == kCFCompareEqualTo)
This is in a helper method that gets called from a couple of different places in the app. The rest of that method up to this point is:

Code:
- (Parent *)makeParentInContext:(NSManagedObjectContext *)context
		  fromAddressBookPerson:(ABRecordRef)personRef
				   withFullName:(BOOL)isFullName
{
   NSLog(@">>> Entering %s <<<", __PRETTY_FUNCTION__);

   Parent *parentToReturn = nil;

   CFStringRef firstNameRef = ABRecordCopyValue(personRef, kABPersonFirstNameProperty);
   CFStringRef lastNameRef = ABRecordCopyValue(personRef, kABPersonLastNameProperty);
	
   ABMultiValueRef phoneMulti = ABRecordCopyValue(personRef, kABPersonPhoneProperty);
   NSMutableArray *phoneArray = [(id)ABMultiValueCopyArrayOfAllValues(phoneMulti) autorelease];
	
   NSString *homePhoneNumber = nil;
   NSString *workPhoneNumber = nil;
   NSString *mobilePhoneNumber = nil;
   NSString *otherPhoneNumber = nil;
	
   for (int i = 0; i < [phoneArray count]; i++)
   {
      CFStringRef labelRef = ABMultiValueCopyLabelAtIndex(phoneMulti, i);
      CFStringRef valueRef = ABMultiValueCopyValueAtIndex(phoneMulti, i);
		
      if (CFStringCompare(labelRef, kABHomeLabel, 0) == kCFCompareEqualTo)
      {
         NSLog(@"Found the home phone number: %@", (NSString *)valueRef);
         homePhoneNumber = [[[(NSString *)valueRef copy] autorelease] stringByRemovingNull];
         if ([homePhoneNumber length] == 0)
         {
            homePhoneNumber = nil;
         }
      }
      else if (CFStringCompare(labelRef, kABPersonPhoneMobileLabel, 0) == kCFCompareEqualTo)
      {
         NSLog(@"Found the mobile phone number: %@", (NSString *)valueRef);
         mobilePhoneNumber = [[[(NSString *)valueRef copy] autorelease] stringByRemovingNull];
         if ([mobilePhoneNumber length] == 0)
         {
            mobilePhoneNumber = nil;
         }
      }
      else if (CFStringCompare(labelRef, kABWorkLabel, 0) == kCFCompareEqualTo)
      {
         NSLog(@"Found the work phone number: %@", (NSString *)valueRef);
         workPhoneNumber = [[[(NSString *)valueRef copy] autorelease] stringByRemovingNull];
         if ([workPhoneNumber length] == 0)
         {
            workPhoneNumber = nil;
         }
      }
      else if (CFStringCompare(labelRef, kABOtherLabel, 0) == kCFCompareEqualTo)
      {
         NSLog(@"Found the other phone number: %@", (NSString *)valueRef);
         otherPhoneNumber = [[[(NSString *)valueRef copy] autorelease] stringByRemovingNull];
         if ([otherPhoneNumber length] == 0)
         {
            otherPhoneNumber = nil;
         }
      }
		
      CFRelease(labelRef);
      CFRelease(valueRef);
   }
	
   ABMultiValueRef emailMulti = ABRecordCopyValue(personRef, kABPersonEmailProperty);
   NSMutableArray *emailArray = [(id)ABMultiValueCopyArrayOfAllValues(emailMulti) autorelease];
	
   NSString *homeEmail = nil;
   NSString *workEmail = nil;
   NSString *otherEmail = nil;
	
   for (int i = 0; i < [emailArray count]; i++)
   {
      CFStringRef labelRef = ABMultiValueCopyLabelAtIndex(emailMulti, i);
      CFStringRef valueRef = ABMultiValueCopyValueAtIndex(emailMulti, i);
		
      if (CFStringCompare(labelRef, kABHomeLabel, 0) == kCFCompareEqualTo)
      {
         NSLog(@"Found the home email: %@", (NSString *)valueRef);
         homeEmail = [[[(NSString *)valueRef copy] autorelease] stringByRemovingNull];
         if ([homeEmail length] == 0)
         {
            homeEmail = nil;
         }
      }
         else if (CFStringCompare(labelRef, kABWorkLabel, 0) == kCFCompareEqualTo)
      {
         NSLog(@"Found the work email: %@", (NSString *)valueRef);
         workEmail = [[[(NSString *)valueRef copy] autorelease] stringByRemovingNull];
         if ([workEmail length] == 0)
         {
            workEmail = nil;
         }
      }
      else if (CFStringCompare(labelRef, kABOtherLabel, 0) == kCFCompareEqualTo)
      {
         NSLog(@"Found the other email: %@", (NSString *)valueRef);
         otherEmail = [[[(NSString *)valueRef copy] autorelease] stringByRemovingNull];
         if ([otherEmail length] == 0)
         {
            otherEmail = nil;
         }
      }
		
      CFRelease(labelRef);
      CFRelease(valueRef);
   }
...
Red line is the line mentioned above. User in question has provided several crash logs, all crashing on that line. I don't know if the user is picking the same contact each time.

An example of where this is called:

Code:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
   NSLog(@">>> Entering %s <<<", __PRETTY_FUNCTION__);
	
   CFStringRef firstNameRef = ABRecordCopyValue(person, kABPersonFirstNameProperty);
   CFStringRef lastNameRef = ABRecordCopyValue(person, kABPersonLastNameProperty);
	
   DataController *dataController = [DataController sharedDataController];
   NSManagedObjectContext *context = [dataController managedObjectContext];
	
   Person *aPerson = [[[Person alloc] initWithEntity:[dataController personEntity] insertIntoManagedObjectContext:context] autorelease];
   [aPerson setFirstName:[(NSString *)firstNameRef stringByRemovingNull]];
   [aPerson setLastName:[(NSString *)lastNameRef stringByRemovingNull]];
	
   Parent *parent = [dataController makeParentInContext:context
            fromAddressBookPerson:person
            withFullName:NO];
...
Basically what this is doing is looking for any phone numbers or email addresses, extracting them if found, and keeping track of any special labels like "Home", "Work", etc.

I have tried sample contacts with no emails, contact with multiple emails tagged as "Home", and just regular contacts with a single email. All work fine for me. Any thoughts on what the issue might be? As far as we know, the vast majority of users are running just fine. Feel free to laugh and point out issues, I'm sure this code is pretty naive. I really wish there was an Obj-C API for the address book stuff.
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Old 01-24-2012, 08:29 AM   #2 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default

Let's see if the morning crew has any thoughts.
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Old 01-25-2012, 09:02 AM   #3 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default

There seems to be a correlation to using MS Exchange. If Exchange is on, crash. If Exchange is off, no crash, but no contacts either. I'm not finding anything in the documentation suggesting how to know that you're dealing with Exchange, and thus no tips on special handling. My Googling only found one or two hits suggesting a possible correlation, but there was no mention of a solution or workaround.

Any thoughts?
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Old 01-29-2012, 10:20 PM   #4 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default

A few beta testers will hopefully prove here shortly, but it looks like I've arrived at an initial solution. The problem is a couple of things, one being my lack of familiarity with CoreFoundation stuff, the other being a quirk with Exchange combined with the suckiness of plain C.

So on the CoreFoundation front, let's take a sample of code:

Code:
CFStringRef labelRef = ABMultiValueCopyLabelAtIndex(phoneMulti, i);
This function can return NULL. I guess that's not inherently an issue in and of itself, but unlike Obj-C which can send messages to nil all day long and not care, doing stuff with NULL in plain C is a big ole' load of hurt. Even something "simple" like memory management. This function has "copy" in the name, therefore it should be released later, which would look like this:

Code:
CFRelease(labelRef);
If the function returned NULL, this line will crash. So you have to do lots of protection checking:

Code:
if (labelRef != NULL)
   CFRelease(labelRef);
And this is when you want to do unspeakable things to whoever at Apple decided not to provide an Obj-C interface to the address book.

As far as Exchange is concerned, we finally got our hands on an Exchange account, and this made it much easier to figure out what was going on. As stated previously, I'm fishing for emails and phone numbers, and trying to maintain any labels. If the address is a "home" email address in the address book, I would still like to identify it as a "home" email address within my app. So I do this:

Code:
ABMultiValueRef emailMulti = ABRecordCopyValue(personRef, kABPersonEmailProperty);
if (emailMulti != NULL)  // <--- Woo, awesome!
   {
      for (int i = 0; i < ABMultiValueGetCount(emailMulti); i++)
      {
         CFStringRef labelRef = ABMultiValueCopyLabelAtIndex(emailMulti, i);
         CFStringRef valueRef = ABMultiValueCopyValueAtIndex(emailMulti, i);

         if (CFStringCompare(labelRef, kABHomeLabel, 0) == kCFCompareEqualTo);
...
The label is the type ("home"), the value is the value ("sample@sample.com"). There are standard constants for finding home, work, other, etc.

What seems to be happening is that when the account was configured to use Exchange, the email address came back without a label. labelRef is NULL. valueRef is not NULL. Viewing the contact within the Contacts app revealed a visual clue to this. Instead of saying "Home", "Work", etc. it simply said "Email". You couldn't even tap on the cell to change it to something else. If you added another one, it also simply said "Email". And for whatever reason in this case, labelRef became NULL. I can only assume that Apple did not provide a constant for this case. And because it was NULL, the CFStringCompare() bombed hard.

So as a protective measure, I first added a test:

Code:
if ( (labelRef != NULL) && (valueRef != NULL) )
This prevented the app from crashing. However, I also wasn't grabbing addresses anymore, since this test failed. To cover the case described above, I added an additional test:

Code:
else if ( (labelRef == NULL) && (valueRef != NULL) )
In this case we are choosing to map this address to "Other".

I have no idea why we did not encounter some issue along these lines in a non-Exchange situation. I guess the standard address book is better about not returning NULL. At any rate, ignorance on my part + oddity on Apple's part = unhappy Brian (and client). I have no idea if this is common to Exchange accounts everywhere, or if there is some server-side setting that may or may not be flipped.

Since you can't have a value without a key in a NSDictionary, I'm sure that if there was an Obj-C API for the address book, someone would have figured this crap out sooner because they would have needed to provide a key of some kind even in a case like this. Oh well. Maybe in iOS 6.
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 385
11 members and 374 guests
7twenty7, Atatator, condor304, FrankWeller, glenn_sayers, iphonedevshani, MAMN84, mraalex, QuantumDoja, tim0504, VinceYuan
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,674
Threads: 94,122
Posts: 402,907
Top Poster: BrianSlick (7,990)
Welcome to our newest member, Atatator
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 05:48 AM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0