博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift 4 前后 KVO 的变化
阅读量:6420 次
发布时间:2019-06-23

本文共 3096 字,大约阅读时间需要 10 分钟。

如果了解过设计模式的同学,应该都知道有一种设计模式叫做观察者模式,属于行为型模式,即当对象存在一对多的依赖关系,当一个对象发生变化时,需要自动通知它的依赖对象。通常用于实时事件处理。

我们来研究一下 iOS 里对观察者模式的支持,即 KVO(key-value observing) ,键值对观察,其原理是基于 KVC(key-value-coding)runtime。通过 Swfit 研究。

KVO 使用

由于 Swift4 之后 KVOapi 有所改变,所以先来看看 Swift4 之前使用 KVO

class Test: NSObject {    dynamic var field = "field"}复制代码
var test = Test()override func viewDidLoad() {    super.viewDidLoad()    test.addObserver(self, forKeyPath: "field", options: [.new, .initial], context: nil)    test.field = "change"}override func observeValue(forKeyPath keyPath: String?,                           of object: Any?,                           change: [NSKeyValueChangeKey : Any]?,                           context: UnsafeMutableRawPointer?) {    print(change)}deinit {    test.removeObserver(self, forKeyPath: "field")}复制代码

在Swift4之前使用 KVO ,即需要在 deinit 中调用 removeObserver ,否则会crash,还需要重写 NSObject

func observeValue(forKeyPath keyPath: String?,                   of object: Any?,                   change: [NSKeyValueChangeKey : Any]?,                   context: UnsafeMutableRawPointer?)复制代码

以完成更多的操作。

而在Swift4中,KVO的 API 变得友好很多。

@objcMembers class Test: NSObject {    dynamic var field = "field"}var observer: NSKeyValueObservation!    override func viewDidLoad() {        super.viewDidLoad()        let test = Test()        observer = test.observe(\.field, options: [.new, .initial]) { (object, change) in            print(change)        }        test.field = "change"    }复制代码

通过闭包优化了 KVO 的实现,在官方WWDC视频上What's New in Foundation专题上介绍 KVO 时,表示该函数会返回一个 NSKeyValueObservation,所以只需要管理这个实例的生命周期,不再需要移除观察者,再也不用担心忘记移除观察者而导致crash。(ps: 可以尝试在方法内声明 NSKeyValueObservation 对象,可以发现即使改变了属性值也不会调用闭包内的操作。 因为随着方法的结束,这个实例和闭包的生命周期都结束了。)

在闭包中第一个参数是对被观察者的引用,防止在闭包内使用被观察者而导致循环引用的问题(相当nice)。

遗憾的是只有继承于 NSObject 的对象才能够使用 KVO

KVO 原理

Swift 4 之前

采用断点调试

addObserver 之前

addObserver 之后

可见,在 addObserver 之后, test 实例会将isa指针指向 NSKVONotifying_Test 的派生类

再通过runtime来具体看看这个两个类的区别

采用辅助函数,查看
test 类和方法的变化。

func find() {    print(NSStringFromClass(object_getClass(test)!))    print(String(describing: class_getName(object_getClass(test))))    var count: UInt32 = 0    let methodlist = class_copyMethodList(object_getClass(test), &count)    for i in 0..

addObserver之前

addObserver之后

从上图输出结果可见,在 addObserver 之后类发生了改变,并且添加了一个私有属性 _isKVOA,从名字可以推测是用于对类标示,以此来标示是 KVO

从图中可以看出 NSKVONotifying_Test 重写了被观察属性 fieldset 方法(即 setField: )。再来看看具体是怎么重写的。

根据 KVOapi 有手动调用的方法。

func willChangeValue(forKey key: String)func didChangeValue(forKey key: String)复制代码

可以推测是在 set 方法内添加 func willChangeValue(forKey key: String)func didChangeValue(forKey key: String)

通过堆栈信息具体看一看调用情况。

第一次调用时 initial ,第二次是属性发生变化时调用的。从堆栈信息一目了然。

Swift 4 之后

接下来我们来探讨下,Swift4 之后 KVO的新 API ,具体的底层原理。 先根据上面的方法测试下是否是通过 runtime 新增 NSKVONotifying_Test 派生类实现的。

从测试结果可见,与 Swift4 之前的原理一致。但是区别在于 NSKVONotifying_Test 的生命周期由 NSKeyValueObservation 管理,通过断点调试看看 NSKeyValueObservation

从上图可见,
NSKeyValueObservation 内有一个
object 属性 是指向观察者
test
callback 回调闭包,以及
path 代指被观察的属性。

从堆栈信息,可以一目了然的看到当被观察属性发生改变时,调用情况。 NSKeyValueObservation 作为了观察者和消息转发者,接收通知和通知 test 的属性发生改变,从而调用 闭包 内的具体操作。

才疏学浅,如有什么理解不到位的欢迎指出。

转载地址:http://kalra.baihongyu.com/

你可能感兴趣的文章
Javascript模块化编程
查看>>
atitit.提升2--3倍开发效率--cbb体系的建设..
查看>>
微软职位内部推荐-SDE
查看>>
myeclipse集成weblogicserver
查看>>
Maven的第一个小程序
查看>>
人机交互的新方向:智能聊天机器人
查看>>
sys--system-sysdba-sysoper用户区别
查看>>
asp.net 后台获取flv视频地址进行播放
查看>>
查看macbook是多少位
查看>>
ASP.NET 保存txt文件
查看>>
【课程分享】深入浅出嵌入式linux系统移植开发 (环境搭建、uboot的移植、嵌入式内核的配置与编译)...
查看>>
ASCII码表及键盘码表。
查看>>
angular学习笔记(二十三)-$http(1)-api
查看>>
CentOS 65 java 访问 MS SQL
查看>>
Oracle11g 搭建单实例DataGuard (转载)
查看>>
tar + find
查看>>
如何设置基线网络
查看>>
位运算符 优先级 折半搜索
查看>>
如何安全地关闭MySQL实例
查看>>
JavaFx初探
查看>>