观察者模式
kvo机制是观察者模式的具体实现,kvo就是键值观察机制,一个对象可以注册为另一个对象的观察者来检测对方的属性的变化并作出响应操作。
纯swift其实没有这个 玩意,因为这东西是oc的,swift要想使用kvo/kvc的话就必须继承自NSobject暴露给oc,所以swift的结构体和枚举就用不了(因为无法继承)。
虽然有局限性,但是有时候真的能够带来方便。比如说,在某些情况下,一个属性的值取决于另一个对象中的一个或者多个属性值时候,利用KVO的特效就相当的方便了。
使用KVO
的基本步骤是:
- 调用
addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)
- 重写函数
observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
- 在不需要观察属性的时候移除
removeObserver(_ observer: NSObject, forKeyPath keyPath: String)
- 如果观察者被多次移除,程序崩溃
- 多次添加观察者,程序崩溃
- 观察者被deinit了,但没有移除监听,程序崩溃
引用:
KVO
是通过一种叫isa-swizzling
技术实现的。什么是isa-swizzling
呢?顾名思义,isa
指针指向维护调度表的对象类,调度表基本包含了指向该类的实现方法的指针,以及其他数据。当观察者注册对象的属性时,观察对象的isa
指针被修改,指向中间类而不是真正的类。通俗的讲,每个类对象都有一个isa
指针,该指针指向当前类,当这个类对象的属性被观察时,系统会动态生成一个中间类(派生类),然后把这个isa
指针指向这个中间类,在这个中间类重写被观察属性的setter
方法,在这个中间类重写的setter
方法实现真正的通知机制。这个通知机制又使用了willChangeValueForKey
(Swift中willChangeValue(forKey key: String)
)和didChangeValueForKey
(Swift中didChangeValue(forKey key: String)
)这两个方法。
优点:
缺点:
代理模式
一个对象A想要实现某些功能,但是不想自己做,另一个类B刚好可以实现这个功能。所以A(老板)可以定义个delegate属性,请B来做,但是B来做的前提是遵循某个协议。举例:
女人有个gohome回家方法。但是女人不会开车,就设置一个代理,让这个代理来drive()
class women: human{
var delegate:drivedelegate?
init(delegate:drivedelegate) {
self.delegate = delegate
}
func shopping(){
print("购物")
}
func gohome(){
guard delegate != nil else {
return
}
delegate?.drive()
}
}
protocol drivedelegate {
func drive()
}
class man:human,drivedelegate{
func drive(){
print("开车")
}
}
直接将delegate用弱引用修饰会报错因为weak与弱引用计数有关,只能修饰对象,不能修饰协议限制的any。
解决:
protocol drivedelegate:class{
func drive()
}//加个class
@objc protocol drivedelegate{
func drive()
}
protocol drivedelegate:NSObjectProtocol{
func drive()
}
优点:职责清晰,易扩展
缺点:只能一对一
单例模式
一个对象在整个生命周期中只实现一个实例,例如创建一个类,类里面有个属性的类型是当前这个类,并且他是一个static或者class修饰的类型属性,然后这个类的构造方法还是private私有的,这样就可以实现一个单例类。
class danli{
static let standard:danli = danli(name: "fmy")
var name:String
private init(name:String){
self.name = name
}
}
danli.standard.name = "666"
优点:易于定位
缺点:
1.不利于扩展.
2.由于单例类在运行过程中一直占用内存资源,在闲置的时候并不会被销毁,所以闲置时也消耗了内存资源