APAddressBook icon indicating copy to clipboard operation
APAddressBook copied to clipboard

Crash in APAddressBook.m on iPhone 6

Open king7532 opened this issue 9 years ago • 24 comments

  • APAddressBook/Core (0.1.11)
  • APAddressBook/Swift (0.1.11)

My app crashes running on my iPhone 6 with a large AddressBook but does not crash in the iOS 6 simulator with only a few entries in APAddressBook.m:117

peopleArrayRef = ABAddressBookCopyArrayOfAllPeople(self.addressBook);

Because:

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000003
Triggered by Thread:  6
Thread 6 name:  Dispatch queue: com.apple.root.default-qos
Thread 6 Crashed:
0   libsqlite3.dylib                0x000000019749006c 0x197444000 + 311404
1   libsqlite3.dylib                0x00000001974900cc 0x197444000 + 311500
2   libsqlite3.dylib                0x000000019748110c 0x197444000 + 250124
3   libsqlite3.dylib                0x000000019747fdf4 sqlite3_step + 524
4   AppSupport                      0x000000018b5e7728 CPSqliteStatementSendResults + 72
5   AppSupport                      0x000000018b5ecd08 CPRecordStoreProcessRecordStatementWithPropertyIndices + 188
6   AppSupport                      0x000000018b5ed084 CPRecordStoreProcessQueryWithBindBlock + 128
7   AppSupport                      0x000000018b5ed150 CPRecordStoreCopyAllInstancesOfClassWhereWithBindBlock + 136
8   AddressBook                     0x000000018454ea28 ABCCopyArrayOfAllPeopleInSourceWithSortOrdering + 156
9   APAddressBook                   0x00000001002b77b8 __48-[APAddressBook loadContactsOnQueue:completion:]_block_invoke (APAddressBook.m:117)
10  AddressBook                     0x00000001845a3194 __37-[ABTCC accessRequestWithCompletion:]_block_invoke + 44
11  libdispatch.dylib               0x000000019776d990 _dispatch_call_block_and_release + 20
12  libdispatch.dylib               0x000000019776d950 _dispatch_client_callout + 12
13  libdispatch.dylib               0x000000019777a77c _dispatch_root_queue_drain + 1844
14  libdispatch.dylib               0x000000019777bc48 _dispatch_worker_thread3 + 104
15  libsystem_pthread.dylib         0x000000019794d228 _pthread_wqthread + 812
16  libsystem_pthread.dylib         0x000000019794ceec start_wqthread + 0

When the debugger breaks on frame 9 above:

(lldb) po self.addressBook
<ABCAddressBook 0x144d0b970 [0x197dc0f50]>

(lldb) po self
<APAddressBook: 0x1700533e0>

I enabled NSZombies objects in my scheme and it did nothing, but i noticed another thread with exeactly the same stack frames, see threads 2 and 6 below:

Hardware Model:      iPhone7,2
Process:             ABC [2876]
Path:                /private/var/mobile/Containers/Bundle/Application/8E7B40EE-A4C2-4E09-8303-501BA3CF2FCC/ABC.app/ABC
Identifier:          com.king7532.abc.ABC
Version:             99 (0.65)
Code Type:           ARM-64 (Native)
Parent Process:      launchd [1]

Date/Time:           2015-07-14 11:13:33.753 -0400
Launch Time:         2015-07-14 11:13:30.530 -0400
OS Version:          iOS 8.4 (12H143)
Report Version:      105

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000003
Triggered by Thread:  6

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0:
0   UIKit                           0x000000018a006ba8 -[UIViewController _isDeallocating] + 0
1   libobjc.A.dylib                 0x000000019710a4b8 -[NSObject allowsWeakReference] + 20
2   libobjc.A.dylib                 0x00000001971037e4 weak_register_no_lock + 140
3   libobjc.A.dylib                 0x00000001971082cc objc_storeWeak + 212
4   ABC                             0x00000001000eb5bc -[ZLPeoplePickerViewController setDelegate:] (ZLPeoplePickerViewController.h:48)
5   ABC                             0x00000001001445a0 ABC.ABCContactsViewController.showPeoplePickerController (ABC.ABCContactsViewController)() -> () (ABCContactsViewController.swift:53)
6   ABC                             0x0000000100146004 ABC.ABCContactsViewController.accessGrantedForAddressBook (ABC.ABCContactsViewController)() -> () (ABCContactsViewController.swift:185)
7   ABC                             0x00000001001457e0 ABC.ABCContactsViewController.checkAddressBookAccess (ABC.ABCContactsViewController)() -> () (ABCContactsViewController.swift:153)
8   ABC                             0x000000010014441c ABC.ABCContactsViewController.viewDidAppear (ABC.ABCContactsViewController)(Swift.Bool) -> () (ABCContactsViewController.swift:45)
9   ABC                             0x000000010014446c @objc ABC.ABCContactsViewController.viewDidAppear (ABC.ABCContactsViewController)(Swift.Bool) -> () (ABCContactsViewController.swift:0)
10  UIKit                           0x0000000189eb3f50 -[UIViewController _setViewAppearState:isAnimating:] + 588
11  UIKit                           0x0000000189eb44bc -[UIViewController _endAppearanceTransition:] + 340
12  UIKit                           0x000000018a1def1c __97-[UITabBarController transitionFromViewController:toViewController:transition:shouldSetSelected:]_block_invoke749 + 32
13  UIKit                           0x0000000189f20268 -[UIViewController _executeAfterAppearanceBlock] + 60
14  UIKit                           0x0000000189f201d0 _applyBlockToCFArrayCopiedToStack + 352
15  UIKit                           0x0000000189e906a4 _afterCACommitHandler + 572
16  CoreFoundation                  0x00000001853dc2a0 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 28
17  CoreFoundation                  0x00000001853d922c __CFRunLoopDoObservers + 356
18  CoreFoundation                  0x00000001853d960c __CFRunLoopRun + 832
19  CoreFoundation                  0x00000001853052d0 CFRunLoopRunSpecific + 392
20  GraphicsServices                0x000000018ed5b6f8 GSEventRunModal + 164
21  UIKit                           0x0000000189f02f3c UIApplicationMain + 1484
22  ABC                             0x000000010012d97c main (AppDelegate.swift:15)
23  libdyld.dylib                   0x000000019779aa04 start + 0

Thread 1 name:  Dispatch queue: com.apple.libdispatch-manager
Thread 1:
0   libsystem_kernel.dylib          0x0000000197898c24 kevent64 + 8
1   libdispatch.dylib               0x000000019777de6c _dispatch_mgr_invoke + 272
2   libdispatch.dylib               0x000000019776f998 _dispatch_mgr_thread + 48

Thread 2 name:  Dispatch queue: com.apple.root.default-qos
Thread 2:
0   libsqlite3.dylib                0x000000019748ffa4 0x197444000 + 311204
1   libsqlite3.dylib                0x000000019748110c 0x197444000 + 250124
2   libsqlite3.dylib                0x000000019748110c 0x197444000 + 250124
3   libsqlite3.dylib                0x000000019747fdf4 sqlite3_step + 524
4   AppSupport                      0x000000018b5e7728 CPSqliteStatementSendResults + 72
5   AppSupport                      0x000000018b5ecd08 CPRecordStoreProcessRecordStatementWithPropertyIndices + 188
6   AppSupport                      0x000000018b5ed084 CPRecordStoreProcessQueryWithBindBlock + 128
7   AppSupport                      0x000000018b5ed150 CPRecordStoreCopyAllInstancesOfClassWhereWithBindBlock + 136
8   AddressBook                     0x000000018454ea28 ABCCopyArrayOfAllPeopleInSourceWithSortOrdering + 156
9   APAddressBook                   0x00000001002b77b8 __48-[APAddressBook loadContactsOnQueue:completion:]_block_invoke (APAddressBook.m:117)
10  AddressBook                     0x00000001845a3194 __37-[ABTCC accessRequestWithCompletion:]_block_invoke + 44
11  libdispatch.dylib               0x000000019776d990 _dispatch_call_block_and_release + 20
12  libdispatch.dylib               0x000000019776d950 _dispatch_client_callout + 12
13  libdispatch.dylib               0x000000019777a77c _dispatch_root_queue_drain + 1844
14  libdispatch.dylib               0x000000019777bc48 _dispatch_worker_thread3 + 104
15  libsystem_pthread.dylib         0x000000019794d228 _pthread_wqthread + 812
16  libsystem_pthread.dylib         0x000000019794ceec start_wqthread + 0

Thread 3:
0   libsystem_kernel.dylib          0x00000001978b3c78 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x000000019794d2d8 _pthread_wqthread + 988
2   libsystem_pthread.dylib         0x000000019794ceec start_wqthread + 0

Thread 4:
0   libsystem_kernel.dylib          0x00000001978b3c78 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x000000019794d2d8 _pthread_wqthread + 988
2   libsystem_pthread.dylib         0x000000019794ceec start_wqthread + 0

Thread 5:
0   libsystem_kernel.dylib          0x00000001978b3c78 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x000000019794d2d8 _pthread_wqthread + 988
2   libsystem_pthread.dylib         0x000000019794ceec start_wqthread + 0

Thread 6 name:  Dispatch queue: com.apple.root.default-qos
Thread 6 Crashed:
0   libsqlite3.dylib                0x000000019749006c 0x197444000 + 311404
1   libsqlite3.dylib                0x00000001974900cc 0x197444000 + 311500
2   libsqlite3.dylib                0x000000019748110c 0x197444000 + 250124
3   libsqlite3.dylib                0x000000019747fdf4 sqlite3_step + 524
4   AppSupport                      0x000000018b5e7728 CPSqliteStatementSendResults + 72
5   AppSupport                      0x000000018b5ecd08 CPRecordStoreProcessRecordStatementWithPropertyIndices + 188
6   AppSupport                      0x000000018b5ed084 CPRecordStoreProcessQueryWithBindBlock + 128
7   AppSupport                      0x000000018b5ed150 CPRecordStoreCopyAllInstancesOfClassWhereWithBindBlock + 136
8   AddressBook                     0x000000018454ea28 ABCCopyArrayOfAllPeopleInSourceWithSortOrdering + 156
9   APAddressBook                   0x00000001002b77b8 __48-[APAddressBook loadContactsOnQueue:completion:]_block_invoke (APAddressBook.m:117)
10  AddressBook                     0x00000001845a3194 __37-[ABTCC accessRequestWithCompletion:]_block_invoke + 44
11  libdispatch.dylib               0x000000019776d990 _dispatch_call_block_and_release + 20
12  libdispatch.dylib               0x000000019776d950 _dispatch_client_callout + 12
13  libdispatch.dylib               0x000000019777a77c _dispatch_root_queue_drain + 1844
14  libdispatch.dylib               0x000000019777bc48 _dispatch_worker_thread3 + 104
15  libsystem_pthread.dylib         0x000000019794d228 _pthread_wqthread + 812
16  libsystem_pthread.dylib         0x000000019794ceec start_wqthread + 0

Thread 7:
0   libsystem_kernel.dylib          0x00000001978b3c78 __workq_kernreturn + 8
1   libsystem_pthread.dylib         0x000000019794d2d8 _pthread_wqthread + 988
2   libsystem_pthread.dylib         0x000000019794ceec start_wqthread + 0

Any help will be greatly appreciated. Thanks

king7532 avatar Jul 14 '15 15:07 king7532

@belkevich Can you please look at this crash, since you modified it use a local queue instead of the main queue and I'm wondering if that has something to dot with it? I'm happy to help debug, thanks

king7532 avatar Jul 14 '15 15:07 king7532

Hi!

  1. Please, show the code of APAddressBook initialization and using.
  2. Do you use APAddressBook in different threads/queues?

belkevich avatar Jul 14 '15 15:07 belkevich

  1. I'm using ZLPeoplePickerViewController which init's and then loads the contacts in ZLAddressBook.m:
//
//  ZLAddressBook.m
//  ZLPeoplePickerViewControllerDemo
//
//  Created by Zhixuan Lai on 11/11/14.
//  Copyright (c) 2014 Zhixuan Lai. All rights reserved.
//

#import "ZLAddressBook.h"
#import "APAddressBook.h"
#import "APContact.h"

NSString *const ZLAddressBookDidChangeNotification =
    @"ZLAddressBookDidChangeNotification";

@interface ZLAddressBook ()
@property (strong, nonatomic) APAddressBook *addressBook;
@property (strong, nonatomic, readwrite) NSArray *contacts;
@end
@implementation ZLAddressBook

+ (instancetype)sharedInstance {
    static dispatch_once_t pred = 0;
    __strong static id _sharedObject = nil;
    dispatch_once(&pred, ^{ _sharedObject = [[self alloc] init]; });
    return _sharedObject;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        [self setup];
    }
    return self;
}

- (void)setup {
    self.addressBook = [[APAddressBook alloc] init];
}

#pragma mark - APAddressBook

- (void)loadContacts:(void (^)(BOOL succeeded, NSError *error))completionBlock {
    __weak __typeof(self) weakSelf = self;
    self.addressBook.fieldsMask =
        APContactFieldFirstName | APContactFieldLastName |
        APContactFieldCompositeName | APContactFieldPhones |
        APContactFieldThumbnail | APContactFieldRecordID |
        APContactFieldEmails | APContactFieldAddresses;
    self.addressBook.filterBlock = ^BOOL(APContact *contact) {
        return contact.compositeName != nil;
    };
    [self.addressBook loadContacts:^(NSArray *contacts, NSError *error) {
        if (!error) {
            weakSelf.contacts = contacts;
            if (completionBlock) {
                completionBlock(YES, nil);
            }
        } else {
            if (completionBlock) {
                completionBlock(NO, error);
            }
        }
    }];
    [self.addressBook startObserveChangesWithCallback:^{
        //        [weakSelf reloadData];
        [[NSNotificationCenter defaultCenter]
            postNotificationName:ZLAddressBookDidChangeNotification
                          object:nil];
    }];
}

@end

2nd point: No I am not using APAddressBook anywhere else in my app.

Ben

king7532 avatar Jul 14 '15 16:07 king7532

I figured out why my app was crashing, ZLPeoplePickerViewController was calling (void)loadContacts:(void (^)(BOOL succeeded, NSError *error))completionBlock above, twice during initialization of ZLPeoplePickerViewController which conversely was calling APAddressBook loadContacts:^(NSArray *contacts, NSError *error) again while it was already in the block and therefore the could not write to peopleArrayRef again because of the __block modifier?

__block CFArrayRef peopleArrayRef;
peopleArrayRef = ABAddressBookCopyArrayOfAllPeople(self.addressBook);

If you're interested I can produce a sample app that will reproduce the problem?

king7532 avatar Jul 14 '15 19:07 king7532

Check out my branch:

https://github.com/king7532/ZLPeoplePickerViewController/tree/reproduce-APAddressBook-0-11-crash

of ZLPeoplePickerViewController and run the ZLPeopePickerViewControllerDemo xcodeworkspace to reproduce the crash (do not run pod install or update).

Also set a breakpoint in APAddressBook.m:112 and observe it being called twice and crashing after stepping thru the 2nd call on line 117.

king7532 avatar Jul 14 '15 20:07 king7532

Thank you! I'll check it out

belkevich avatar Jul 15 '15 07:07 belkevich

How to reproduce the crash? Your example works fine on my device.

belkevich avatar Jul 15 '15 14:07 belkevich

What device and how many contacts?

king7532 avatar Jul 15 '15 15:07 king7532

iPhone 4s, 133

belkevich avatar Jul 16 '15 08:07 belkevich

This is definitely a race condition, because I also cannot reproduce it on my iPhone 6 simulator with just 6 contacts. But on my iPhone 6 (arm64) with 960 contacts it happens every time.

king7532 avatar Jul 16 '15 12:07 king7532

First time loadContact method called from here

+ (void)initializeAddressBook {
    [[ZLAddressBook sharedInstance] loadContacts:nil];
}

Looks like author wanted to preload all contacts and cache them. And your local address book is too heavy. And cache load doesn't get in time before you start load contacts again by pressing 'show people picker' button.

belkevich avatar Jul 16 '15 13:07 belkevich

That's it.

king7532 avatar Jul 16 '15 17:07 king7532

So, how can I help you?))

belkevich avatar Jul 17 '15 07:07 belkevich

Just jumping in here…I wanted to see if there is a solution to this crash, as our app has also experienced this crash as well.

djfitz avatar Jul 23 '15 16:07 djfitz

My solution was to stop using APAddressBook and create my own AddressBookManager class encapsulating an AddressBookRef that I use throughout my app.

king7532 avatar Jul 23 '15 17:07 king7532

@djfitz I see 4 possible solutions:

  1. Just remove this "cache" code from ZLPeoplePickerViewController. Anyway it doesn't work when it really needs (on big address books).
  2. Wrap APAddressBook calls to serial queue.
  3. Use you own solution like @king7532
  4. Wait until we make APAddressBook thread safe.

A bit more information about pt. 4. It's not easy, because we can't use dispatch_queue/NSOperationQueue. Old Core Foundation functions doesn't work with them properly. See this issue. So we need to rewrite all addressBook calls with NSThread. Unfortunately I don't know when it will be done.

belkevich avatar Jul 24 '15 15:07 belkevich

removing this helped for sure. its grabbing too many contacts all at once to be realistic.

+ (void)initializeAddressBook { [[ZLAddressBook sharedInstance] loadContacts:nil]; }

ericlewis avatar Jul 30 '15 00:07 ericlewis

@ericlewis is right. All this contacts will use app memory for all time because it's singleton.

belkevich avatar Jul 30 '15 08:07 belkevich

Didn't really notice a performance hit either when we removed it. So I certainly recommend. I'll also do a pull request against that FW so you don't have to mess with it.

ericlewis avatar Jul 30 '15 08:07 ericlewis

It would be great! Thank you!

belkevich avatar Jul 30 '15 08:07 belkevich

Can we expect this to be threadSafe any time soon?

spidergears avatar Sep 07 '15 06:09 spidergears

For thread safe and concurrent Address Book operations take a look at https://github.com/danthorpe/Operations#addressbook

king7532 avatar Sep 07 '15 13:09 king7532

@king7532 NSOperation is wrapper on GCD. So, it doesn't have own runloop. This solution doesn't fit us. @spidergears I've found solution with NSThread but it should be tested before I release it. I'm planning to finish this month.

belkevich avatar Sep 07 '15 13:09 belkevich

Hello, New release '0.1.12' it thread safe. Check it. And if everything ok I close the issue

belkevich avatar Sep 22 '15 15:09 belkevich