macOS应用闲置时NSOutlineView持续更新占用40%CPU求助
我来分享几个实战中排查这类问题的思路,应该能帮你定位到根源:
跟踪OutlineView更新的触发源
虽然你观察到只有objectValueForTableColumn:被频繁调用,但本质是OutlineView在反复请求数据源数据。你可以给reloadData、reloadItem:、reloadItemsAtIndexes:inParent:这些OutlineView更新方法加日志断点,或者用Instruments的Time Profiler查看调用栈,找到是谁在触发这些更新——大概率是重构后某个循环、KVO回调或者定时器里不小心加了重复调用的代码。检查KVO通知的异常触发
如果你的OutlineView用了绑定(Bindings),或者数据源对象依赖KVO,那可能某个属性在持续发送KVO通知(哪怕值没有实际变化)。比如重构后某个setter方法被修改,每次调用都强制发送willChangeValueForKey:和didChangeValueForKey:,哪怕新旧值一致。用Instruments的KVO模板跟踪通知发送,就能找到频繁触发的通知源。定位Core Animation提交的原因
你提到主循环一直在处理Core Animation提交,NSOutlineView本身依赖CA绘制,所以大概率是某个单元格视图在持续重绘。比如自定义的NSTableCellView里needsDisplay被频繁设为YES,或者有未停止的隐式动画。用Instruments的Core Animation模板,勾选“Commit”选项,查看每次提交的调用栈,就能找到触发重绘的视图和原因。排查自动布局的循环问题
重构后很容易引入自动布局循环约束,导致布局系统反复计算布局,间接触发OutlineView的单元格更新。打开Xcode的Debug View Hierarchy检查布局警告,或者用Instruments的Auto Layout模板跟踪布局计算频率,看是否有异常频繁的布局任务。检查隐藏的定时器或Run Loop源
有可能重构后不小心添加了重复触发的NSTimer,或者自定义的Run Loop源在后台持续运行,间接触发了OutlineView的数据源查询。用Time Profiler查看主线程的调用栈,找那些每秒执行20次左右的任务,顺着调用栈就能找到定时器或Run Loop源。验证数据源对象的稳定性
objectValueForTableColumn:每秒调用20次,说明OutlineView认为数据源在变化。你可以在这个方法里加日志,记录每次调用的tableColumn和item,看是不是同一个item和column被反复查询。如果是,检查这个item的相关属性(比如用于排序、显示的属性)是否在频繁变动,甚至是hash值的异常变化,导致OutlineView认为需要重新获取数据。
内容的提问来源于stack exchange,提问作者rdodson

