Swift属性监听

1、属性观察器

Swift 提供了属性观察器来监听自身存储属性的变化。观察器只能用于监听非lazy存储属性。对于计算属性,它已经内含了get{}set{},闭包内已经知道属性的变化,不需要再提供观察器。对于从父类继承下来的存储属性或计算属性,我们可以在子类中重写属性的gettersetter来为它们添加属性观察器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student{

var nick : String {
willSet{
print("++++New value:\(newValue)")
}
didSet{
print("++++Old value:\(oldValue)")
}
}
init(name:String) {
self.nick = name
}
}

调用及输出结果:

1
2
3
4
5
var st = Student(name: "HALO")
st.nick = "james"
//输出
++++New value:james
++++Old value:HALO

观察器只能用来观察自身属性(包括从父类继承下来的属性)的变化。

2、使用KVO观察属性

条件1:观察者和被观察者都要继承自NSObject

KVO 是 OC 中的特性,在 OC 中它是基于 runtime 的动态分发机制和 KVC,通过key来监听value的变化。OC 中所有的类都直接或间接继承自 NSObject,而根类 NSObject 默认遵守了NSKeyValueCoding协议,所以这些类才能实现 KVO。Swift class 只有继承了 NSObject 才能拥有这些特性。对于没有父类或者不继承 NSObject 的情况,则可以使用上面提到的观察器来实现此功能。

条件2:需要将属性标记为@objcdynamic

@objc标记用来将 Swift 类中的属性暴露给 OC;你也可以将类标注为@objcMembers,使用这种标注的类会隐式地为类中所有的属性和方法添加@objc标注。

另外,Swift 中默认关闭了动态派发机制,声明为@objc的属性或方法有可能会被 Swift 优化为静态调用,不一定会动态派发,所以还需将属性标记为dynamic才能开启运行时从而监听属性的变化。

方案1(addObserver:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class People: NSObject {

//定义待监听的属性
@objc dynamic var name = "defaultName"
//更新属性值
func updateName(pName:String) -> () {
name = pName
}

//注册监听
override init() {
super.init()
addObserver(self, forKeyPath: "name", options: [.new,.old], context: nil)
}

//处理监听
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
if keyPath == "name", let newName = change?[.newKey] {
print("++++name updated to: \(newName)")
}else{
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}

//析构中移除监听
deinit {
removeObserver(self, forKeyPath: "name", context: nil)
print("++++已移除监听")
}
}

调用方法与输出结果:

1
2
3
4
5
6
7
8
//调用:
let people = People()
people.updateName(pName: "Dav")
people.setValue("Hello", forKey: "name")
//输出结果:
++++name updated to: Dav
++++name updated to: Hello
++++已移除监听

方案2(observe闭包)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class People: NSObject {

//定义待监听的属性
@objc dynamic var name = "defaultName"
//更新属性值
func updateName(pName:String) -> () {
name = pName
}
}

class Observer: NSObject {

@objc var objToObserve : People //注意这里也要标记为@objc

var observation : NSKeyValueObservation?

//注册监听
init(object : People) {
objToObserve = object
super.init()

observation = observe(\.objToObserve.name,options: [.old, .new], changeHandler: { (object, change) in
print("name updated from: \(change.oldValue!), updated to: \(change.newValue!)")
})
}

deinit {
print("++++Observer Deinited")
}
}

调用方法与输出结果如下:

1
2
3
4
5
6
7
8
9
//调用:
let people = People()
let observer = Observer(object: people)
people.updateName(pName: "Dav")
people.setValue("Hello", forKey: "name")
//输出结果:
++++name updated from: defaultName, updated to: Dav
++++name updated from: Dav, updated to: Hello
++++Observer Deinited

相关参考

#©swift官方文档

#©自己实现KVO


Swift属性监听
https://davidlii.cn/2018/08/31/swift-kvo.html
作者
Davidli
发布于
2018年8月31日
许可协议