Search code examples
objective-cmacosdebuggingnscollectionviewnstabview

Mystery observer in crash of NSCollectionView embedded in NSTabView


I have an NSCollectionView embedded in one of the tabs of an NSTabView. The collection is loaded and displays correctly, but upon switching to any other tab, the app crashes with the following crash log. The crash notes that an observer (ProjectTabLeadersCollection, which is the class of my NSCollectionView) cannot be removed for the key path "firstResponder". I'm not sure, exactly, what's going on, as I'm not explicitly registering any observers for this tab, so I'm assuming that AppKit is doing something behind the scenes, and my setup within either Interface Builder or my initialization code isn't quite correct. Hoping someone might have some guidance here.

Any ideas?

2022-04-12 07:21:53.861124-0700 APBA Baseball[78639:4002869] [General] Cannot remove an observer <ProjectTabLeadersCollection 0x7f9e6b8df1e0> for the key path "firstResponder" from <NSWindow 0x7f9e7c044500> because it is not registered as an observer.
2022-04-12 07:21:53.864095-0700 APBA Baseball[78639:4002869] [General] (
    0   CoreFoundation                      0x00007ff809bf41e3 __exceptionPreprocess + 242
    1   libobjc.A.dylib                     0x00007ff809954c13 objc_exception_throw + 48
    2   Foundation                          0x00007ff80a9bf1ff -[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:] + 700
    3   Foundation                          0x00007ff80a9beeff -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] + 129
    4   AppKit                              0x00007ff80c77f736 -[NSCollectionView viewWillMoveToWindow:] + 274
    5   AppKit                              0x00007ff80c5b449a -[NSView _setWindow:] + 277
    6   AppKit                              0x00007ff80ce04884 __21-[NSView _setWindow:]_block_invoke.425 + 299
    7   CoreAutoLayout                      0x00007ff811211cf3 -[NSISEngine withBehaviors:performModifications:] + 84
    8   AppKit                              0x00007ff80c5b4b04 -[NSView _setWindow:] + 1919
    9   AppKit                              0x00007ff80ce04884 __21-[NSView _setWindow:]_block_invoke.425 + 299
    10  CoreAutoLayout                      0x00007ff811211cf3 -[NSISEngine withBehaviors:performModifications:] + 84
    11  AppKit                              0x00007ff80c5b4b04 -[NSView _setWindow:] + 1919
    12  AppKit                              0x00007ff80c62f07e -[NSScrollView _setWindow:] + 93
    13  AppKit                              0x00007ff80ce04884 __21-[NSView _setWindow:]_block_invoke.425 + 299
    14  CoreAutoLayout                      0x00007ff811211cf3 -[NSISEngine withBehaviors:performModifications:] + 84
    15  AppKit                              0x00007ff80c5b4b04 -[NSView _setWindow:] + 1919
    16  AppKit                              0x00007ff80c5e3d8f -[NSView removeFromSuperview] + 158
    17  AppKit                              0x00007ff80c7a230f __56-[NSView replaceSubview:with:options:completionHandler:]_block_invoke + 40
    18  AppKit                              0x00007ff80c7a2106 -[NSView replaceSubview:with:options:completionHandler:] + 735
    19  AppKit                              0x00007ff80c77996b -[NSTabView _switchTabViewItem:oldView:withTabViewItem:newView:initialFirstResponder:lastKeyView:] + 859
    20  AppKit                              0x00007ff80c772e21 -[NSTabView selectTabViewItem:] + 512
    21  AppKit                              0x00007ff80c7791b3 -[NSTabViewController setSelectedTabViewItemIndex:] + 637
    22  AppKit                              0x00007ff80c7a16fe -[NSApplication(NSResponder) sendAction:to:from:] + 288
    23  AppKit                              0x00007ff80c7a15a4 -[NSControl sendAction:to:] + 86
    24  AppKit                              0x00007ff80c7a14d6 __26-[NSCell _sendActionFrom:]_block_invoke + 131
    25  AppKit                              0x00007ff80c7a13df -[NSCell _sendActionFrom:] + 171
    26  AppKit                              0x00007ff80c89c311 -[NSSegmentedCell _sendActionFrom:] + 161
    27  AppKit                              0x00007ff80c79e19f NSControlTrackMouse + 1813
    28  AppKit                              0x00007ff80c79da66 -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 121
    29  AppKit                              0x00007ff80c89ba45 -[NSSegmentedCell trackMouse:inRect:ofView:untilMouseUp:] + 712
    30  AppKit                              0x00007ff80c79cd06 -[NSControl mouseDown:] + 678
    31  AppKit                              0x00007ff80c79b1f1 -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 4859
    32  AppKit                              0x00007ff80c70f39e -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 2582
    33  AppKit                              0x00007ff80c70e76e -[NSWindow(NSEventRouting) sendEvent:] + 352
    34  AppKit                              0x00007ff80c70cb44 -[NSApplication(NSEvent) sendEvent:] + 352
    35  AppKit                              0x00007ff80c9c596b -[NSApplication _handleEvent:] + 65
    36  AppKit                              0x00007ff80c58e35e -[NSApplication run] + 623
    37  AppKit                              0x00007ff80c5622b7 NSApplicationMain + 817
    38  APBA Baseball                       0x00000001058f8e22 main + 34
    39  dyld                                0x0000000109b1951e start + 462

My tab view is setup like so: Tabless Tab View

The NSCollection is inside a tab called Leaders, and connections appear like so: NSCollectionView

Here is the header for the collection:

@interface ProjectTabLeadersCollection : NSCollectionView <NSCollectionViewDataSource, NSCollectionViewDelegate> {

    NSMutableArray  *ar;
}
@end

Next, the .m file:

#import "ProjectTabLeadersCollection.h"
#import "ProjectTabLeadersCollectionItem.h"

@implementation ProjectTabLeadersCollection

-(void) viewDidMoveToWindow {
    self.delegate   = self;
    self.dataSource = self;
    ar = [[NSMutableArray alloc] init];
    for (int i = 0; i < 100; ++i)
         [ar addObject:@"Hello"];
}

- (void)collectionView:(NSCollectionView *)collectionView willDisplayItem:(nonnull NSCollectionViewItem *)item forRepresentedObjectAtIndexPath:(nonnull NSIndexPath *)indexPath {
    
}

- (void)collectionView:(NSCollectionView *)collectionView didEndDisplayingItem:(nonnull NSCollectionViewItem *)item forRepresentedObjectAtIndexPath:(nonnull NSIndexPath *)indexPath {
    
}

- (NSInteger) numberOfSectionsInCollectionView:(NSCollectionView *)collectionView {
    return (1);
}

- (NSInteger) collectionView:(NSCollectionView *)collectionView numberOfItemsInSection:(NSInteger)section  {
    return (ar.count);
}

- (NSCollectionViewItem *) collectionView:(NSCollectionView *)collectionView itemForRepresentedObjectAtIndexPath:(NSIndexPath *)indexPath
{
    ProjectTabLeadersCollectionItem *item = [collectionView makeItemWithIdentifier:@"ProjectTabLeadersCollectionItem" forIndexPath:indexPath];
    item->StatNameLabel.stringValue = [ar objectAtIndex:[indexPath item]];
    return (item);
    
//    ProjectTabStandingsCollectionItem   *item = [collectionView makeItemWithIdentifier:@"ProjectTabStandingsCollectionItem" forIndexPath:indexPath];
//    ProjectTeamObject                   *pto = [standings objectAtIndex:[indexPath item]];
//
//    item->StandingsLabel.stringValue = pto.profile.city;
//    return (item);
}


- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    
    // Drawing code here.
}

@end

Solution

  • The documentation of viewDidMoveToWindow states

    The default implementation does nothing

    but [NSCollectionView viewDidMoveToWindow] is implemented and starts observing firstResponder of the window. -[ProjectTabLeadersCollection viewDidMoveToWindow] must call super.