如何为Swift数组添加可感知变更索引的Property Observer?
嘿,这个需求挺常见的——原生Swift数组的didSet确实只能告诉你数组发生了变化,但没法直接拿到具体变更的索引。不过我们可以自己封装一个带索引追踪的数组类型,来实现这个功能。
实现带索引追踪的数组(支持获取变更索引)
原生Array的didSet观察器只能感知到数组整体被修改,但无法获取具体的变更位置和类型(添加、删除、修改)。要精准追踪索引,最可靠的方式是封装一个自定义数组容器,在内部处理数组操作时记录变更信息,再通过自定义逻辑通知外部。
方法一:自定义数组结构体 + 变更回调
我们可以写一个TrackableArray结构体,内部持有原生数组,并重写常用的数组操作方法,在操作时主动记录对应的索引和变更类型:
struct TrackableArray<Element> { // 内部存储的原生数组 private var internalArray: [Element] = [] // 自定义变更回调,传递变更类型和对应索引 var onArrayChange: ((ChangeType, Int) -> Void)? // 定义变更类型枚举 enum ChangeType { case added case removed case modified } // 模拟原生数组下标 subscript(index: Int) -> Element { get { return internalArray[index] } set { internalArray[index] = newValue onArrayChange?(.modified, index) } } // 模拟append方法 mutating func append(_ element: Element) { let newIndex = internalArray.count internalArray.append(element) onArrayChange?(.added, newIndex) } // 模拟insert方法 mutating func insert(_ element: Element, at index: Int) { internalArray.insert(element, at: index) onArrayChange?(.added, index) } // 模拟remove(at:)方法 mutating func remove(at index: Int) -> Element { let removedElement = internalArray.remove(at: index) onArrayChange?(.removed, index) return removedElement } // 可按需扩展其他数组方法,比如removeLast、replaceSubrange等 }
使用示例
var myTrackableArray = TrackableArray<String>() myTrackableArray.onArrayChange = { changeType, index in switch changeType { case .added: print("在索引\(index)处添加了元素") case .removed: print("从索引\(index)处删除了元素") case .modified: print("索引\(index)处的元素被修改了") } } myTrackableArray.append("Hello") // 输出:在索引0处添加了元素 myTrackableArray.insert("World", at: 0) // 输出:在索引0处添加了元素 myTrackableArray[1] = "Swift" // 输出:索引1处的元素被修改了 myTrackableArray.remove(at: 0) // 输出:从索引0处删除了元素
方法二:基于原生数组didSet的折中方案(局限性大)
如果一定要用原生数组的didSet,只能通过对比新旧数组的差异来推断索引,但这种方法有明显缺陷:无法精准区分变更类型,批量修改时效率低,且容易出错。
示例代码:
var myArray: [String] = [] { didSet { let oldCount = oldValue.count let newCount = myArray.count // 数组长度增加(添加元素) if newCount > oldCount { let addedIndex = newCount - 1 print("在索引\(addedIndex)处添加了元素") } // 数组长度减少(删除元素) else if newCount < oldCount { // 遍历找第一个差异位置 for index in 0..<min(oldCount, newCount) { if oldValue[index] != myArray[index] { print("索引\(index)处的元素被删除或修改") break } } if min(oldCount, newCount) == newCount { print("从索引\(newCount)处删除了元素") } } // 数组长度不变(修改元素) else { for index in 0..<newCount { if oldValue[index] != myArray[index] { print("索引\(index)处的元素被修改了") } } } } }
注意
这种方法仅适用于简单的单元素操作,一旦涉及批量修改(比如replaceSubrange),就无法准确追踪所有变更索引,所以更推荐第一种自定义数组的方案。
内容的提问来源于stack exchange,提问作者Marmelador
相关产品推荐
相关产品推荐

