Reputation: 1078
I use NSCollectionView
with Content Array (Legacy) layout, so I need setContent:
only to change the collection view items while they are build by a NSLibraryGridItem
subclass and a .xib file. However, some of my users are receiving crashes when setContent:
is called, but very rarely. This is the crash log:
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Application Specific Information:
objc[5520]: garbage collection is OFF
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[NSViewController loadView] could not load the "(null)" nib.'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff843b4f56 __exceptionPreprocess 198
1 libobjc.A.dylib 0x00007fff895bfd5e objc_exception_throw 43
2 CoreFoundation 0x00007fff843b4d8a [NSException raise:format:arguments:] 106
3 CoreFoundation 0x00007fff843b4d14 [NSException raise:format:] 116
4 AppKit 0x00007fff8630b1b5 -[NSViewController loadView] 336
5 AppKit 0x00007fff86306dfe -[NSViewController view] 41
6 AppKit 0x00007fff8694c425 -[NSCollectionViewItem _copyConnectionsToItem:] 49
7 AppKit 0x00007fff8694c9d9 -[NSCollectionViewItem copyWithZone:] 541
8 CoreFoundation 0x00007fff8435cd54 -[NSObject copy] 20
9 AppKit 0x00007fff869560c7 -[NSCollectionView newItemForRepresentedObject:] 63
10 AppKit 0x00007fff8695047a -[NSCollectionView _getItemsToDisplay] 1406
11 AppKit 0x00007fff869568f4 -[NSCollectionView setContent:] 275
12 MyApp 0x00000001068a1879 _mh_execute_header 112761
13 libdispatch.dylib 0x00007fff89486a82 _dispatch_call_block_and_release 18
14 libdispatch.dylib 0x00007fff894888f2 _dispatch_main_queue_callback_4CF 308
15 CoreFoundation 0x00007fff84349e7c __CFRunLoopRun 1724
16 CoreFoundation 0x00007fff84349486 CFRunLoopRunSpecific 230
17 HIToolbox 0x00007fff89b172bf RunCurrentEventLoopInMode 277
18 HIToolbox 0x00007fff89b1e56d ReceiveNextEventCommon 355
19 HIToolbox 0x00007fff89b1e3fa BlockUntilNextEventMatchingListInMode 62
20 AppKit 0x00007fff861da779 _DPSNextEvent 659
21 AppKit 0x00007fff861da07d -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] 135
22 AppKit 0x00007fff861d69b9 -[NSApplication run] 470
23 AppKit 0x00007fff86452eac NSApplicationMain 867
24 MyApp 0x0000000106887d38 _mh_execute_header 7480
25 ??? 0x0000000000000002 0x0 2
)
terminate called throwing an exception
abort() called
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libsystem_kernel.dylib 0x00007fff89fdfce2 __pthread_kill 10
1 libsystem_c.dylib 0x00007fff86e8e7d2 pthread_kill 95
2 libsystem_c.dylib 0x00007fff86e7fa7a abort 143
3 libc abi.dylib 0x00007fff8753c7bc abort_message 214
4 libc abi.dylib 0x00007fff87539fcf default_terminate() 28
5 libobjc.A.dylib 0x00007fff895c01b9 _objc_terminate 94
6 libc abi.dylib 0x00007fff8753a001 safe_handler_caller(void (*)()) 11
7 libc abi.dylib 0x00007fff8753a05c std::terminate() 16
8 libc abi.dylib 0x00007fff8753b152 __cxa_throw 114
9 libobjc.A.dylib 0x00007fff895bfe7a objc_exception_throw 327
10 com.apple.CoreFoundation 0x00007fff843b4d8a [NSException raise:format:arguments:] 106
11 com.apple.CoreFoundation 0x00007fff843b4d14 [NSException raise:format:] 116
12 com.apple.AppKit 0x00007fff8630b1b5 -[NSViewController loadView] 336
13 com.apple.AppKit 0x00007fff86306dfe -[NSViewController view] 41
14 com.apple.AppKit 0x00007fff8694c425 -[NSCollectionViewItem _copyConnectionsToItem:] 49
15 com.apple.AppKit 0x00007fff8694c9d9 -[NSCollectionViewItem copyWithZone:] 541
16 com.apple.CoreFoundation 0x00007fff8435cd54 -[NSObject copy] 20
17 com.apple.AppKit 0x00007fff869560c7 -[NSCollectionView newItemForRepresentedObject:] 63
18 com.apple.AppKit 0x00007fff8695047a -[NSCollectionView _getItemsToDisplay] 1406
19 com.apple.AppKit 0x00007fff869568f4 -[NSCollectionView setContent:] 275
20 MyApp 0x00000001068a1879 0x106886000 112761
21 libdispatch.dylib 0x00007fff89486a82 _dispatch_call_block_and_release 18
22 libdispatch.dylib 0x00007fff894888f2 _dispatch_main_queue_callback_4CF 308
23 com.apple.CoreFoundation 0x00007fff84349e7c __CFRunLoopRun 1724
24 com.apple.CoreFoundation 0x00007fff84349486 CFRunLoopRunSpecific 230
25 com.apple.HIToolbox 0x00007fff89b172bf RunCurrentEventLoopInMode 277
26 com.apple.HIToolbox 0x00007fff89b1e56d ReceiveNextEventCommon 355
27 com.apple.HIToolbox 0x00007fff89b1e3fa BlockUntilNextEventMatchingListInMode 62
28 com.apple.AppKit 0x00007fff861da779 _DPSNextEvent 659
29 com.apple.AppKit 0x00007fff861da07d -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] 135
30 com.apple.AppKit 0x00007fff861d69b9 -[NSApplication run] 470
31 com.apple.AppKit 0x00007fff86452eac NSApplicationMain 867
32 MyApp 0x0000000106887d38 0x106886000 7480
With me the app works normally, and the users that received the crash were only doing regular things, which means that there is apparently no special condition that causes the crash, and that I can't reproduce the bug.
An important information: my app needs to support macOS 10.6, so I can only use solutions that support that system.
EDIT (30/08/2017):
The problem still wasn't solved. I've made that to avoid the crash, but I still don't know the origin of it:
-(BOOL)safeSetContent:(NSArray*)newContent
{
@try
{
[self setContent:newContent];
[self setNeedsDisplay:YES];
return YES;
}
@catch (NSException* exception)
{
if (exception.name == NSInternalInconsistencyException)
{
[NSAlert showAlertOfType:NSAlertTypeError withMessage:@"An unexpected NSInternalInconsistencyException occured."];
return NO;
}
[NSAlert showAlertMessageWithException:exception];
return NO;
}
}
It's important to say that I don't use NSViewController
in any part of my code (the project was created during the Mac OS X 10.6 era, and it wasn't default back there).
And these are three functions that call safeSetContent:
:
-(void)forceReloadTableContents
{
NSArray* contentArray = self.appsArray.tabType == DEFAULT_TYPE ? @[DEFAULT_VALUE] : nil;
[NSThread dispatchBlockInMainQueue:^
{
if ([self safeSetContent:contentArray])
{
[NSThread dispatchQueueWithName:THREAD_RELOAD_GRID_VIEW_AFTER_SAVE
priority:DISPATCH_QUEUE_PRIORITY_DEFAULT concurrent:NO withBlock:^
{
[self reloadTableContents];
}];
}
}];
}
-(void)reloadTableWithType:(NSInteger)type
{
[self safeSetContent:@[]];
if (type == DEFAULT_TYPE)
{
[NSThread detachNewThreadSelector:@selector(loadDefaultType) toTarget:self.libraryTabView withObject:nil];
}
else
{
[(NSLibraryTabView*)self.libraryTabView loadOtherType];
}
}
-(void)reloadTableContents
{
BOOL isServer = (self.appsArray.tabType == DEFAULT_TYPE);
NSMutableArray* contentArray = isServer ? [@[DEFAULT_VALUE] mutableCopy] : [@[] mutableCopy];
if (self.appsArray.array) [contentArray addObjectsFromArray:self.appsArray.array];
if (contentArray.count == 0) contentArray = nil;
[NSThread dispatchBlockInMainQueue:^
{
if ([self safeSetContent:contentArray])
{
[[NSNotificationCenter defaultCenter] postNotificationName:COLLECTION_VIEW_UPDATE_NOTIFICATION object:nil];
}
}];
}
I've changed the name of some variables.
The bug has been observed in multiple macOS versions, from 10.6 to 10.12.
I guess this is my NSCollectionViewItem
subclass relevant content:
@implementation NSLibraryGridItem
-(NSDictionary*)representedDictionary
{
if (!self.representedObject) return nil;
return (NSDictionary*)[self.representedObject copy];
}
-(void)setRepresentedObject:(id)representedObject
{
[super setRepresentedObject:representedObject];
NSDictionary* info = self.representedDictionary;
if (info)
{
if ([info isKindOfClass:[NSDictionary class]])
{
// App logic
}
}
}
@end
And NSLibraryGridItem
is chosen as the view during the collection view awakeFromNib
with that:
self.collectionViewItem = [NSLibraryGridItem new];
[self setItemPrototype:self.collectionViewItem];
Upvotes: 0
Views: 996
Reputation: 17060
According to the documentation, the behavior of NSViewController
which causes it to automatically load a xib file with the same name as the NSViewController
subclass is only documented to work reliably on OS X 10.10 and higher, whereas on older versions it results in undefined behavior:
Since you are targeting 10.6, you need to specify the nib name manually. You can do this by adding an override to the nibName
property in your NSCollectionViewItem
subclass. This should, I think, fix your problem, since the exception you're getting is complaining about not knowing the name of the nib it needs to load, and since "undefined behavior" certainly seems to describe the non-reproducibility of your problem.
Upvotes: 1