1

I have a ViewController which fetch data in AddressBook. To be notified when data has been fetched, I post a notification to this ViewController to allow it to update a NSViewTable (in the main queue).This ViewController is presented with a transient NSPopover, when a user ends editing a NSTextField in the ViewController view.

When I enter a first string in this field, the popover is displayed then is closed when the user has finishing interacting with it. If I edit again the Text Field, the Popover do not appear in view, but the fetching process starts. When it ends and the DataUpdated method devoted to the notification post is fired. Then I got a crash on the dispatch_sync(dispatch_get_main_queue(), ^{ line itself (no message in console, even with NSZombie enabled). Here is the code in the popover content ViewController:

- (void) getContactForName: (NSString *) name
{

    CNEntityType entityType = CNEntityTypeContacts;
    if ([CNContactStore authorizationStatusForEntityType:entityType]){
        CNContactStore *store = [[CNContactStore alloc] init];
        [store requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _nullableError){
            if (granted) {
                if ([CNContactStore class]){
                    NSError *contactError;
                    CNContactStore *addressBook = [[CNContactStore alloc] init];
                    [addressBook containersMatchingPredicate:[CNContainer predicateForContainersWithIdentifiers:@[addressBook.defaultContainerIdentifier]] error:&contactError];
                    NSArray * keysToFetch =@[CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactFamilyNameKey, CNContactGivenNameKey, CNContactIdentifierKey];
                    NSError *error;

                    contactList = [addressBook unifiedContactsMatchingPredicate:[CNContact predicateForContactsMatchingName:name] keysToFetch:keysToFetch error:&error];
                    [self DataUpdated];
                }
            }
        }];

    }

}
-(void) receiveDataNotification: (NSNotification *) notification
{
    [table reloadData];
}
- (void) DataUpdated
{
    dispatch_sync(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdateNotification" object:self];
    });
}
- (void) viewWillAppear
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(receiveDataNotification:)
                                                 name:@"DataUpdateNotification"
                                               object:nil];
    [table  setDoubleAction:@selector(doubleClick:)];
}

Any idea of what's happen? The error message in the source code is Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

The Backtrace is:

#0  0x000000010034c266 in _dispatch_barrier_sync_f_slow ()
#1  0x00000001000095e6 in -[ContactViewController DataUpdated] at /myDirectoryPath/ContactViewController.m:46
#2  0x0000000100009ea4 in __43-[ContactViewController getContactForName:]_block_invoke at /myDirectoryPath/ContactViewController.m:88
#3  0x00007fffae259a29 in -[CNFutureCompletionBlocks addSuccessBlock:orCallWithFutureResult:] ()
#4  0x00007fffa28eeb4a in -[CNContactStore requestAccessForEntityType:completionHandler:] ()
#5  0x0000000100009b30 in -[ContactViewController getContactForName:] at /myDirectoryPath/ContactViewController.m:78
#6  0x00000001000132dc in -[MainViewController getContactsForName:] at /myDirectoryPath/MainViewController.m:437
#7  0x0000000100013391 in -[MainViewController controlTextDidEndEditing:] at /myDirectoryPath/MainViewController.m:444
#8  0x00007fffa2ee854c in __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ ()
#9  0x00007fffa2ee844b in _CFXRegistrationPost ()
#10 0x00007fffa2ee81b2 in ___CFXNotificationPost_block_invoke ()
#11 0x00007fffa2ea6782 in -[_CFXNotificationRegistrar find:object:observer:enumerator:] ()
#12 0x00007fffa2ea576b in _CFXNotificationPost ()
#13 0x00007fffa48e7677 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#14 0x00007fffa0b96081 in -[NSTextField textDidEndEditing:] ()
#15 0x00007fffa2ee854c in __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ ()
#16 0x00007fffa2ee844b in _CFXRegistrationPost ()
#17 0x00007fffa2ee81b2 in ___CFXNotificationPost_block_invoke ()
#18 0x00007fffa2ea6782 in -[_CFXNotificationRegistrar find:object:observer:enumerator:] ()
#19 0x00007fffa2ea576b in _CFXNotificationPost ()
#20 0x00007fffa48e7677 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#21 0x00007fffa0d22099 in -[NSTextView(NSPrivate) _giveUpFirstResponder:] ()
#22 0x00007fffa0b822b7 in -[NSTextView doCommandBySelector:] ()
#23 0x00007fffa0b821d1 in -[NSTextInputContext(NSInputContext_WithCompletion) doCommandBySelector:completionHandler:] ()
#24 0x00007fffa0b5fe00 in -[NSKeyBindingManager(NSKeyBindingManager_MultiClients) interpretEventAsCommand:forClient:] ()
#25 0x00007fffa13a17b1 in __84-[NSTextInputContext _handleEvent:options:allowingSyntheticEvent:completionHandler:]_block_invoke.1096 ()
#26 0x00007fffa13a15e5 in __84-[NSTextInputContext _handleEvent:options:allowingSyntheticEvent:completionHandler:]_block_invoke_3 ()
#27 0x00007fffa0b67f59 in -[NSTextInputContext tryHandleEvent_HasMarkedText_withDispatchCondition:dispatchWork:continuation:] ()
#28 0x00007fffa13a1560 in __84-[NSTextInputContext _handleEvent:options:allowingSyntheticEvent:completionHandler:]_block_invoke.1086 ()
#29 0x00007fffa246489f in __TSMProcessRawKeyEventWithOptionsAndCompletionHandler_block_invoke_5 ()
#30 0x00007fffa24636e2 in ___ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec_block_invoke ()
#31 0x00007fffa139a4f8 in __55-[NSTextInputContext handleTSMEvent:completionHandler:]_block_invoke.314 ()
#32 0x00007fffa0b61835 in __55-[NSTextInputContext handleTSMEvent:completionHandler:]_block_invoke_2 ()
#33 0x00007fffa0b617b4 in -[NSTextInputContext tryHandleTSMEvent_HasMarkedText_withDispatchCondition:dispatchWork:continuation:] ()
#34 0x00007fffa0b6119f in -[NSTextInputContext handleTSMEvent:completionHandler:] ()
#35 0x00007fffa0b60883 in _NSTSMEventHandler ()
#36 0x00007fffa2409d85 in DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) ()
#37 0x00007fffa2408ff6 in SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) ()
#38 0x00007fffa2408e3f in SendEventToEventTargetWithOptions ()
#39 0x00007fffa24606d6 in SendTSMEvent_WithCompletionHandler ()
#40 0x00007fffa2460bb1 in __SendUnicodeTextAEToUnicodeDoc_WithCompletionHandler_block_invoke ()
#41 0x00007fffa2460a12 in __SendFilterTextEvent_WithCompletionHandler_block_invoke ()
#42 0x00007fffa2460727 in SendTSMEvent_WithCompletionHandler ()
#43 0x00007fffa2460511 in SendFilterTextEvent_WithCompletionHandler ()
#44 0x00007fffa24601d6 in SendUnicodeTextAEToUnicodeDoc_WithCompletionHandler ()
#45 0x00007fffa245ff8c in __utDeliverTSMEvent_WithCompletionHandler_block_invoke_2 ()
#46 0x00007fffa245fe32 in __utDeliverTSMEvent_WithCompletionHandler_block_invoke ()
#47 0x00007fffa245fbf9 in TSMKeyEvent_WithCompletionHandler ()
#48 0x00007fffa245f948 in __TSMProcessRawKeyEventWithOptionsAndCompletionHandler_block_invoke_4 ()
#49 0x00007fffa245f775 in __TSMProcessRawKeyEventWithOptionsAndCompletionHandler_block_invoke_3 ()
#50 0x00007fffa245f48b in __TSMProcessRawKeyEventWithOptionsAndCompletionHandler_block_invoke_2 ()
#51 0x00007fffa245f1c2 in __TSMProcessRawKeyEventWithOptionsAndCompletionHandler_block_invoke ()
#52 0x00007fffa245e73a in TSMProcessRawKeyEventWithOptionsAndCompletionHandler ()
#53 0x00007fffa13a13d1 in __84-[NSTextInputContext _handleEvent:options:allowingSyntheticEvent:completionHandler:]_block_invoke.1077 ()
#54 0x00007fffa13a0689 in __204-[NSTextInputContext tryTSMProcessRawKeyEvent_orSubstitution:dispatchCondition:setupForDispatch:furtherCondition:doubleSpaceSubstitutionCondition:doubleSpaceSubstitutionWork:dispatchTSMWork:continuation:]_block_invoke.1003 ()
#55 0x00007fffa13a04f3 in -[NSTextInputContext tryTSMProcessRawKeyEvent_orSubstitution:dispatchCondition:setupForDispatch:furtherCondition:doubleSpaceSubstitutionCondition:doubleSpaceSubstitutionWork:dispatchTSMWork:continuation:] ()
#56 0x00007fffa13a0e1b in -[NSTextInputContext _handleEvent:options:allowingSyntheticEvent:completionHandler:] ()
#57 0x00007fffa13a0362 in -[NSTextInputContext _handleEvent:allowingSyntheticEvent:] ()
#58 0x00007fffa0b5f28b in -[NSView interpretKeyEvents:] ()
#59 0x00007fffa0b5f0a1 in -[NSTextView keyDown:] ()
#60 0x00007fffa12c12cc in -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] ()
#61 0x00007fffa12bff0a in -[NSWindow(NSEventRouting) sendEvent:] ()
#62 0x00007fffa11454a8 in -[NSApplication(NSEvent) sendEvent:] ()
#63 0x00007fffa0c210f8 in -[NSApplication _doModalLoop:peek:] ()
#64 0x00007fffa0e01374 in __35-[NSApplication runModalForWindow:]_block_invoke ()
#65 0x00007fffa0c1eb98 in -[NSApplication runModalForWindow:] ()
#66 0x00007fffa2ef222c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ ()
#67 0x00007fffa2ed33c4 in __CFRunLoopDoBlocks ()
#68 0x00007fffa2ed2b35 in __CFRunLoopRun ()
#69 0x00007fffa2ed2544 in CFRunLoopRunSpecific ()
#70 0x00007fffa2431ebc in RunCurrentEventLoopInMode ()
#71 0x00007fffa2431bf9 in ReceiveNextEventCommon ()
#72 0x00007fffa2431b26 in _BlockUntilNextEventMatchingListInModeWithFilter ()
#73 0x00007fffa09caa54 in _DPSNextEvent ()
#74 0x00007fffa11467ee in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] ()
#75 0x00007fffa0c210b6 in -[NSApplication _doModalLoop:peek:] ()
#76 0x00007fffa0e01374 in __35-[NSApplication runModalForWindow:]_block_invoke ()
#77 0x00007fffa0c1eb98 in -[NSApplication runModalForWindow:] ()
#78 0x00007fffa2ef222c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ ()
#79 0x00007fffa2ed33c4 in __CFRunLoopDoBlocks ()
#80 0x00007fffa2ed2b35 in __CFRunLoopRun ()
#81 0x00007fffa2ed2544 in CFRunLoopRunSpecific ()
#82 0x00007fffa2431ebc in RunCurrentEventLoopInMode ()
#83 0x00007fffa2431bf9 in ReceiveNextEventCommon ()
#84 0x00007fffa2431b26 in _BlockUntilNextEventMatchingListInModeWithFilter ()
#85 0x00007fffa09caa54 in _DPSNextEvent ()
#86 0x00007fffa11467ee in -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] ()
#87 0x00007fffa09bf3db in -[NSApplication run] ()
#88 0x00007fffa0989e0e in NSApplicationMain ()
#89 0x000000010001eb42 in main at /myDirectoryPath/main.m:12
#90 0x00007fffb8a9e235 in start ()
#91 0x00007fffb8a9e235 in start ()
4

1 回答 1

5

您的dataUpdated方法用于dispatch_sync调用主线程:

- (void) DataUpdated
{
    dispatch_sync(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdateNotification" object:self];
    });
}

您要确保仅在主线程上发送此通知,这是一个好主意。从回溯中可以清楚地看出,当此方法主线程发送时,就会出现问题。很容易看出为什么这是一个问题。dispatch_sync等到你给它的块完成,但是如果你尝试在主队列上运行块,并且主线程阻塞等待dispatch_sync完成,主线程将永远死锁,你的应用程序基本上挂起。现在,为什么它会崩溃EXC_BAD_INSTRUCTION我不确定,但是当我尝试它时,它也在我的机器上这样做。我很确定它只是在我第一次偶然做这个的时候就挂了,所以谁知道呢。也许他们在某个时候添加了一些保护来检测这种情况并只是崩溃,这样您就可以获得一个很好的回溯来弄清楚发生了什么。

无论如何,我们只需要在等待主线程时避免阻塞主线程。如果你dispatch_async不是dispatch_sync,这会很好用:

- (void) DataUpdated
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdateNotification" object:self];
    });
}

dispatch_async在等待块完成时不会阻塞,因此不会死锁。坏消息是通知不会立即触发;它将等到事件循环的下一次旋转。如果这是一个问题,只需检查主线程,如下所示:

- (void) DataUpdated
{
    // hopefully my skills at remembering the $@#%ing block syntax haven't 
    // atrophied due to primarily writing Swift lately
    void (^sendIt)(void) = ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdateNotification" object:self];
    };   

    if ([NSThread isMainThread]) {
        sendIt();
    } else {
        dispatch_sync(dispatch_get_main_queue(), sendIt);
    }
}

这将确保通知总是立即发送,并且该方法总是等到它完成发送。如果你经常做这种事情,你可以把上面的代码写成一个实用函数,它接受一个任意块并在主线程上同步运行它。

于 2018-05-09T17:30:33.217 回答