Swift TLV编辑器:增删Item时NSOutlineView父Construction长度同步异常
问题排查
1. 目标construction获取逻辑可能出错
删除操作中,你先通过outlineView拿到选中的Item,再调用getConstructionForSelectedItem()反向查找它所属的construction。如果这个方法的实现存在缺陷(比如选中Item时返回了错误的父节点,或者节点层级判断逻辑错误),就会导致从错误的节点开始扣减长度,最终出现同步异常。
对比添加逻辑:添加时是先明确目标construction再执行添加,节点指向清晰;但删除时的反向查找更容易出现偏差。
2. 删除与长度更新的顺序错误
当前删除逻辑是先修改construction.clength -= ctr,再调用viewModel.remove(item: selectedItem, from: construction)。如果viewModel.remove内部存在隐含的长度修改逻辑(比如误操作重复扣减),或者删除操作失败(比如Item不在目标construction的列表中),都会导致长度数据不一致。正确的顺序应该是先执行删除操作,确认成功后再更新长度。
3. 父节点链的完整性问题
遍历父节点更新长度时,依赖construction.parent的正确性。如果在添加construction到上级节点时,没有正确设置parent属性,或者Item归属关系变更时parent链没有同步更新,就会导致上级节点的长度无法被正确修改。
更优解决方案:用计算属性替代手动维护长度
与其每次增删操作都手动遍历更新所有父节点的长度,不如将clength改为计算属性,让它自动根据子项的长度求和,从根源上避免同步问题。
修改Construction类的核心代码:
class Construction: Equatable { var id: Int? // 替换为计算属性,自动计算总长度 var clength: Int { var totalLength = 0 for item in items { if let normalItem = item as? Item { totalLength += normalItem.length } else if let subConstruction = item as? Construction { totalLength += subConstruction.clength } } return totalLength } var parent: Construction? var items = [Any]() // ... 其他原有代码 }
方案优势
- 彻底消除手动维护长度的繁琐逻辑,增删Item、子construction长度变化时,
clength会自动实时计算,从根源解决同步问题。 - 层级嵌套再多也能保证长度正确,父节点的
clength会自动依赖子节点的计算结果。 - 减少代码冗余,降低出错概率。
额外优化建议:在Item类中添加parentConstruction: Construction?属性,当Item被添加到某个construction时直接设置该属性,删除时可直接通过selectedItem.parentConstruction获取所属节点,避免通过outlineView反向查找的潜在错误。
内容的提问来源于stack exchange,提问作者Fritz

