Search code examples
macoscocoadrag-and-dropxcode5objective-c++

Crash during drag operations in C++/Objective-C application on OSX


I have a large C++ application with some Objective-C mixed in, building with XCode 5. One view in the app has a list of items. Dragging and dropping the list items will work fine for 5 or 10 or 50 times, but eventually the app will crash during the dragging. It never crashes when running from XCode (release or debug builds). It doesn't crash when I run the debug build externally. It only crashes running the release build externally.

Here's the snippet that begins the drag operation (the crash occurs before returning from dragImage):

// start drag
m_bDragging = true;
[[pHost getView]
    dragImage:pDragImage
    at:ptOffset.getNSPoint()
    offset:NSZeroSize
    event:pEvent
    pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
    source:[pHost getView]
    slideBack:YES];
m_bDragging = false;

[pHost getView] returns an NSView member variable pointer. It doesn't improve the behavior to provide a NULL for dragImage and event. The pasteboard has a list of file names related to the list items.

Here is a typical crash report (which always have a CFSetGetValue in them):

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000025900000100

VM Regions Near 0x25900000100:
CG shared images       00000001c871f000-00000001c8727000 [   32K] r--/r-- SM=SHM
-->
JS JIT generated code  00002ece70800000-00002ece70801000 [    4K] ---/rwx SM=NUL

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.CoreFoundation        0x00007fff9a8c06a9 CFSetGetValue + 25
1   com.apple.CoreFoundation        0x00007fff9a8d81df __CFRunLoopFindMode + 223
2   com.apple.CoreFoundation        0x00007fff9a902a39 CFRunLoopAddTimer + 153
3   com.apple.HIServices            0x00007fff9265bf15 StartIPCPing + 166
4   com.apple.HIToolbox             0x00007fff986956bf __DragInfoPtrSetValueForKey + 160
5   com.apple.HIToolbox             0x00007fff98695616 __CoreDragSetValueForKey + 52
6   com.apple.AppKit                0x00007fff905c968d __50-[NSDragDestination _resetUpdateDraggingItemTimer]_block_invoke + 164
7   libdispatch.dylib               0x00007fff96beb1bb _dispatch_call_block_and_release + 12
8   libdispatch.dylib               0x00007fff96be828d _dispatch_client_callout + 8
9   libdispatch.dylib               0x00007fff96bf0433 _dispatch_after_timer_callback + 77
10  libdispatch.dylib               0x00007fff96be828d _dispatch_client_callout + 8
11  libdispatch.dylib               0x00007fff96bea885 _dispatch_source_invoke + 413
12  libdispatch.dylib               0x00007fff96befe97 _dispatch_main_queue_callback_4CF + 244
13  com.apple.CoreFoundation        0x00007fff9a9404f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
14  com.apple.CoreFoundation        0x00007fff9a8fb714 __CFRunLoopRun + 1636
15  com.apple.CoreFoundation        0x00007fff9a8fae75 CFRunLoopRunSpecific + 309
16  com.apple.HIToolbox             0x00007fff98634a0d RunCurrentEventLoopInMode + 226
17  com.apple.HIToolbox             0x00007fff986347b7 ReceiveNextEventCommon + 479
18  com.apple.HIToolbox             0x00007fff986345bc _BlockUntilNextEventMatchingListInModeWithFilter + 65
19  com.apple.AppKit                0x00007fff902bb24e _DPSNextEvent + 1434
20  com.apple.AppKit                0x00007fff902ba89b -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
21  com.apple.AppKit                0x00007fff905c9170 NSCoreDragBlockingProc + 107
22  com.apple.HIServices            0x00007fff9265a230 SampleMouseAndKeyboard + 102
23  com.apple.HIServices            0x00007fff92659f50 DragInApplication + 50
24  com.apple.HIServices            0x00007fff92659177 CoreDragStartDragging + 519
25  com.apple.AppKit                0x00007fff905c5a54 -[NSCoreDragManager _dragUntilMouseUp:accepted:] + 862
26  com.apple.AppKit                0x00007fff9077d3a4 -[NSCoreDragManager dragImage:fromWindow:at:offset:event:pasteboard:source:slideBack:] + 1355
27  com.apple.AppKit                0x00007fff90a6385b -[NSWindow(NSDrag) d

Another crash log started like this:

2014-09-04 19:47:59.659 myApp[47690:507] -[__NSCFNumber member:]: unrecognized selector sent to instance 0x5907
2014-09-04 19:47:59.687 myApp[47690:507] An uncaught exception was raised
2014-09-04 19:47:59.688 myApp[47690:507] -[__NSCFNumber member:]: unrecognized selector sent to instance 0x5907
2014-09-04 19:47:59.688 myApp[47690:507] (
    0   CoreFoundation                      0x00007fff9a9d925c __exceptionPreprocess + 172
    1   libobjc.A.dylib                     0x00007fff92958e75 objc_exception_throw + 43
    2   CoreFoundation                      0x00007fff9a9dc12d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
    3   CoreFoundation                      0x00007fff9a937272 ___forwarding___ + 1010
    4   CoreFoundation                      0x00007fff9a936df8 _CF_forwarding_prep_0 + 120
    5   CoreFoundation                      0x00007fff9a8c0721 CFSetGetValue + 145

I'm out of ideas. Any help would be appreciated, thanks.


Solution

  • This is a memory management bug in your code. The second crash log shows a message -member: was sent to an object that doesn't respond to it (an NSNumber), which strongly suggests the target of the message was supposed to be a class that does respond to -member:. This is a big clue as I'll bet that's your own code. Somewhere along the line, you're trying to send a message to an object after it's already been released. Maybe you forgot to retain the dragged thing somewhere? Maybe you overreleased (if you're not using ARC or garbage collection)? Do you have a dangling reference that you didn't nil out? Dunno.

    The tool you should be using is Instruments, with the Zombies instrument. Run the application, make it crash, then let Instruments flag the point wherein you "sent a message to a zombie object" (in this case, the instrument doesn't actually release objects but rather keeps them around and marks them as "zombies" - undead objects - and watches for messages sent to objects that should have been deallocated after their retain count reached zero). Click the arrow in the popover message and pay very close attention to that object's entire lifetime (in terms of where it was created and all retain/release messages sent and from where). This is the only solid way of tracking down a bug of this nature: get it to fail while running under the Zombies instrument in Instruments.