I'm working in Swift (macOS) and have an NSTableView, the contents of which are bound to an NSArrayController. The tableView cells can set/edit the data using a mix of Strings and NSPopUpButtons across the cell's nine columns, which are managed via bindings using a simple NSObject subclass (i.e., with @objc dynamic var
properties). The bindings all seem to work, and the data is correct, but I'm noticing that it gets incredibly slow as the contents of the table get even somewhat large (i.e., a couple hundred items).
Also, putting the app in the background and transitioning back into the foreground causes a spinning wheel hang, the length of which is correlated with the number of items in the tableView.
Does anybody have any tips for where I'd start trying to track down the problem? The slowness seems to have started when I added the NSPopUpButtons to some of the cell columns, but I don't have any clear idea of why that would be. The NSPopUpButton bindings were created in Interface Builder, using Value Selection -> Selected Value
(in the Bindings Inspector).
UPDATE: Removing the bindings makes no difference. From what I can tell, the time is being spent updating the NSPopUpButtons. This is both surprising and unfortunate, as they're really the best UI element for what I'm doing.
61.00 ms 81.3% 0 s -[NSTableCellView _windowChangedKeyState]
61.00 ms 81.3% 0 s -[NSView _windowChangedKeyState]
59.00 ms 78.6% 0 s -[NSPopUpButton _windowChangedKeyState]
59.00 ms 78.6% 0 s -[NSControl _windowChangedKeyState]
56.00 ms 74.6% 0 s +[NSAppearance _performWithCurrentAppearance:usingBlock:]
55.00 ms 73.3% 0 s __35-[NSControl _windowChangedKeyState]_block_invoke
24.00 ms 32.0% 0 s -[NSButton updateCell:]
22.00 ms 29.3% 0 s -[NSPopUpButtonCell _updateSubviewsInView:includeTitleTextField:]
21.00 ms 28.0% 0 s -[NSButtonCell _updateSubviewsInView:includeTitleTextField:]
18.00 ms 24.0% 0 s -[NSButtonCell _updateBackgroundViewWithFrame:inView:]
10.00 ms 13.3% 0 s -[NSButtonCell _bezelRectForBounds:inView:]
7.00 ms 9.3% 0 s _NSSetObjectValueAndNotify
1.00 ms 1.3% 1.00 ms DYLD-STUB$$objc_setProperty_atomic_copy
3.00 ms 4.0% 0 s -[NSView setNeedsDisplay:]
1.00 ms 1.3% 0 s -[NSButtonCell _wantsSeparatedContentSubviewsInView:]
9.00 ms 12.0% 0 s -[NSButtonCell _windowChangedKeyStateInView:]
The other thing that makes this difficult is that, when coming out of the background, there appears to be a pause before Instruments registers any activity in the app. This may just be something with Instruments giving up focus... not sure, but it doesn't help. The sample above is from the initial activity when the app becomes active, but this does not represent the entire duration of the delay, a fair portion of which shows no activity in Instruments at all.
If it really is just the case that using NSPopUpButtons in tableView cell columns is a bad idea, then I'll have to change my design, but if anybody has any further suggestions, I'd appreciate any advice.
UPDATE 2: Taking a bigger sample from the pause, there is actually a good chunk of time spent elsewhere (I'm afraid I don't know enough about view drawing to understand exactly what's going on here):
302.00 ms 99.0% 0 s Main Thread 0x20adcd
302.00 ms 99.0% 0 s start
302.00 ms 99.0% 0 s main
302.00 ms 99.0% 0 s NSApplicationMain
302.00 ms 99.0% 0 s -[NSApplication run]
243.00 ms 79.6% 0 s -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]
243.00 ms 79.6% 0 s _DPSNextEvent
243.00 ms 79.6% 0 s _BlockUntilNextEventMatchingListInModeWithFilter
243.00 ms 79.6% 0 s ReceiveNextEventCommon
243.00 ms 79.6% 0 s RunCurrentEventLoopInMode
243.00 ms 79.6% 0 s CFRunLoopRunSpecific
243.00 ms 79.6% 0 s __CFRunLoopRun
243.00 ms 79.6% 0 s __CFRunLoopDoObservers
243.00 ms 79.6% 0 s __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
243.00 ms 79.6% 0 s __65+[CATransaction(NSCATransaction) NS_setFlushesWithDisplayRefresh]_block_invoke
243.00 ms 79.6% 0 s CA::Transaction::commit()
206.00 ms 67.5% 0 s CA::Context::commit_transaction(CA::Transaction*)
203.00 ms 66.5% 0 s CA::Layer::display_if_needed(CA::Transaction*)
102.00 ms 33.4% 0 s -[_NSViewBackingLayer display]
97.00 ms 31.8% 0 s _NSViewUpdateLayer
92.00 ms 30.1% 0 s -[NSButtonBezelView updateLayer]
91.00 ms 29.8% 0 s -[NSWidgetView updateLayer]
90.00 ms 29.5% 0 s _withOverlaidDictionary
90.00 ms 29.5% 0 s __27-[NSWidgetView updateLayer]_block_invoke
90.00 ms 29.5% 0 s -[NSAppearance _createOrUpdateLayer:options:]
90.00 ms 29.5% 0 s -[NSCompositeAppearance _callCoreUIWithBlock:options:]
88.00 ms 28.8% 0 s CUICreateOrUpdateLayer
88.00 ms 28.8% 0 s CUIRenderer::CreateOrUpdateLayer(__CFDictionary const*, CALayer**)
85.00 ms 27.8% 0 s CUICoreThemeRenderer::CreateOrUpdateLayer(CUIDescriptor const*, CALayer**)
81.00 ms 26.5% 0 s CUICoreThemeRenderer::CreateOrUpdateLayerSimple(CUIRenditionKey*, CUIDescriptor const*, CALayer**)
74.00 ms 24.2% 0 s -[CUIThemeFacet updateLayer:effects:]
71.00 ms 23.2% 0 s -[CUIShapeEffectStack newFlattenedImageFromShapeCGImage:withScale:cache:]
37.00 ms 12.1% 0 s -[CIContext createCGImage:fromRect:format:colorSpace:deferred:]
FINAL UPDATE: So yes, it seems that NSPopUpButton really is the issue. Too Bad. I moved the popups out of the tableView and instead use them to update columns (just textFields) in the selected tableView cell. Speed problem gone.