Weird crash in ABPeoplePicker

2019-03-06 02:15发布

问题:

I'm using ABPeoplePicker and I'm getting a strange crash:

Assertion failure in -[UISearchDisplayController setActive:animated:], 
/SourceCache/UIKit/UIKit-1447.6.4/UISearchDisplayController.m:589
2011-01-26 22:30:37.041 Watches[3784:307] 
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', 
reason: 'search contents navigation controller must not change between -setActive:YES and -setActive:NO

The crash is occurring after the user uses the search field and then picks a resulting candidates. My delegate's peoplePickerNavigationController:shouldContinueAfterSelectingPerson: method is called with the appropriate information for a person. The delegate is itself a modal controller, so it saves the information and calls back to its parent, which calls [self dismissModalViewControllerAnimated:NO] and then returns back down the stack to ABPeoplePicker, returning NO for the shouldContinue.

Setting a breakpoint in ViewWillAppear to the parent routine (the highwater mark), I see a stack that looks like:

#0  -[RunnerListTableViewController viewWillAppear:] (self=0x1cc650, _cmd=0x33bc6b5a, animated=1 '\001') at /Users/hughmackworth/develop/Watches/RunnerListTableViewController.m:61
#1  0x338e4fe4 in -[UINavigationController viewWillAppear:] ()
#2  0x3391a1e2 in -[UITabBarController viewWillAppear:] ()
#3  0x3396e280 in -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:] ()
#4  0x339dd36e in -[UIViewController _dismissModalViewControllerWithTransition:from:] ()
#5  0x339dc86e in -[UIViewController dismissModalViewControllerWithTransition:] ()
#6  0x339dc734 in -[UIViewController dismissModalViewControllerWithTransition:] ()
#7  0x339dc734 in -[UIViewController dismissModalViewControllerWithTransition:] ()
#8  0x339da668 in -[UIViewController dismissModalViewControllerAnimated:] ()
#9  0x000098c2 in -[RunnerListTableViewController runnerAddViewController:didAddRunners:] (self=0x1cc650, _cmd=0x29a86, runnerAddViewController=0x1dbf50, runners=0x45cb860) at /Users/hughmackworth/develop/Watches/RunnerListTableViewController.m:147
#10 0x0000d4a8 in -[RunnerAddNewViewController save:] (self=0x1dbf50, _cmd=0x3530e35b, runner=0x45c8c20) at /Users/hughmackworth/develop/Watches/RunnerAddNewViewController.m:174
#11 0x0000ce6c in -[RunnerAddNewViewController peoplePickerNavigationController:shouldContinueAfterSelectingPerson:] (self=0x1dbf50, _cmd=0x30549382, peoplePicker=0x35c87e0, person=0x455f2c0) at /Users/hughmackworth/develop/Watches/RunnerAddNewViewController.m:105
#12 0x35eabaea in -[ABPeoplePickerNavigationController showCardForPerson:withMemberCell:animate:forceDisableEditing:personViewController:] ()
#13 0x35eab978 in -[ABMembersViewController showCardForPerson:withMemberCell:animate:] ()
#14 0x35eab826 in -[ABMembersController showCardForPerson:withMemberCell:animate:] ()
#15 0x35eab76a in -[ABMembersController abDataSource:selectedPerson:atIndexPath:withMemberCell:animate:] ()
#16 0x35eab70e in -[ABMembersFilteredDataSource tableView:didSelectRowAtIndexPath:] ()
#17 0x3390483a in -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] ()
#18 0x339c4612 in -[UITableView _userSelectRowAtPendingSelectionIndexPath:] ()

I then watch the various routines wind down, until it crashes when the stack is down to:

// removed assertion handling routines>>
4   UIKit              0x3394192b -[UISearchDisplayController setActive:animated:] + 986
5   AddressBookUI      0x35ee65d7 -[ABMembersController cancelSearchingAnimated:] + 26
6   AddressBookUI      0x35eab789 -[ABMembersController abDataSource:selectedPerson:atIndexPath:withMemberCell:animate:] + 80
7   AddressBookUI      0x35eab70f -[ABMembersFilteredDataSource tableView:didSelectRowAtIndexPath:] + 82
8   UIKit              0x3390483b -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 662

It all looks completely proper to me, with the minor exception of the assertion crash.

As I have nothing to do with the search controller inside of ABPeoplePicker, nor changing it during its becoming active or not, I don't see how I can fix this.

As a side-note, I do notice an occasional error: CPSqliteStatementSendResults: interrupted while I'm typing into the search box. Any thoughts on what this means?

The delegate's code is:

- (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
    NSLog(@"got one from peoplepicker");
    if (ABRecordGetRecordType (person) == kABPersonType{ 

    Runner * newRunner = [Runner createRunnerFromAddrBookPerson: person];
    if (newRunner) {
            self.nameTextView.text = newRunner.Name;
            [self save:newRunner];
    }
    return NO;    //EDIT: MUST return YES or this will crash
}

- (void)save: (Runner *) runner { //send back last one added
    [self.delegate runnerAddViewController:self 
                             didAddRunners:[NSArray arrayWithObject:runner]];  
}

And the parent's delegate code is:

- (void)runnerAddViewController:(RunnerAddNewViewController *)runnerAddViewController 
                  didAddRunners:(NSArray *)runners {
    if (runners) {
           //stuff interacting with my model
    }
    // Dismiss the modal addRunner view controller
    [self dismissModalViewControllerAnimated:YES];
}

Any clues or suggestions on experiments to run or where to look? Is the dismissModalVC being called from the right place (to dismiss two levels)?

回答1:

Turns out this is an actual bug. If you do a double ModalVC dismiss to ABPeoplePicker when the user clicks a person in search, you'll get this crash. Fortunately, there's a simple workaround: return YES in your delegate's shouldContinueAfterSelectingPerson. As you're simultaneously dismissing the picker, it doesn't really matter whether you return YES or NO, it won't continue, but NO will crash and YES doesn't.



回答2:

Run with NSZombieEnabled and I suspect you will replace the assertion failure with an error that some object is being accessed after it has been released.