如何通过NSObject.KeyValueObservingPublisher获取新旧值?
解决Combine KVO获取属性新旧值的问题
你当前的代码只能拿到初始值和新值,是因为publisher(for: \.name, options: ...)默认仅输出属性的当前值。要同时获取新旧值,可通过以下两种可靠方法实现:
方法一:利用KVO原生变更信息
使用NSObject的publisher(forKeyPath:options:)方法,它会输出包含完整变更详情的NSKeyValueObservedChange对象,从中可直接提取新旧值:
import Combine class Person: NSObject { @objc dynamic var name: String init(name: String) { self.name = name } } let person = Person(name: "John") var cancellables = Set<AnyCancellable>() person.publisher(forKeyPath: \Person.name, options: [.initial, .new, .old]) .sink(receiveCompletion: { _ in }, receiveValue: { change in if let initialValue = change.initialValue { print("初始值: \(initialValue)") } if let oldValue = change.oldValue, let newValue = change.newValue { print("旧值: \(oldValue), 新值: \(newValue)") } }) .store(in: &cancellables) person.name = "Allen"
输出结果:
初始值: John 旧值: John, 新值: Allen
方法二:用Combine操作符跟踪前值
通过scan操作符存储上一次的属性值,将当前值与前值组合成元组输出,纯Combine风格实现:
import Combine class Person: NSObject { @objc dynamic var name: String init(name: String) { self.name = name } } let person = Person(name: "John") var cancellables = Set<AnyCancellable>() person.publisher(for: \.name, options: [.initial, .new]) .scan((old: nil as String?, new: nil as String?)) { previous, current in (old: previous.new, new: current) } .filter { $0.new != nil } .sink { pair in if let initial = pair.new, pair.old == nil { print("初始值: \(initial)") } else if let old = pair.old, let new = pair.new { print("旧值: \(old), 新值: \(new)") } } .store(in: &cancellables) person.name = "Allen"
输出结果:
初始值: John 旧值: John, 新值: Allen
关键说明
- 方法一直接复用KVO原生变更数据,适合需要完整KVO细节的场景;
- 方法二完全基于Combine操作符实现,更贴合响应式编程风格;
- 务必将订阅者存入
Set<AnyCancellable>,避免订阅因对象提前释放失效。
内容的提问来源于stack exchange,提问作者CocoaUser
相关产品推荐
相关产品推荐

