在swift中实现KVO的思路
我们知道如果不借助NSObject基类是无法实现oc中的KVO的。而KVO的基本原理是很简单的,把每次对属性的get、set转发给订阅者就可以了。但是如果没有编译器和runtime的支持仅使用swift的语法,实际�使用起来还是很不方便的(其实同样的问题在java、c#中早已有之)。一个简单的解决方案是定义一个observable接口去桥接属性的get\set方法与该类外部的订阅者(参考windows phone做MVVM的方式)。
我目前想到的折中解决方案是这样的:
-
利用上篇提到的"关联对象"将对一个属性的get/set 转发到自定义的KVO属性对象上
-
在类的外部利用属性名字符串进行订阅,但为了类型安全代码写起来有点复杂(obj和propName都重复使用了2次)。伪代码类似于这样:
kvo(obj, obj.propName, "propName") { newValue in // callback }
在这里不得不说oc中的宏定义(参见RACObserve宏)和c#中的expression语法才是解决这类问题(自定义DSL)的利器。但是swift中并没有这些,甚至都不能通过反射获取属性的get、set方法。虽然在oc中也是使用字符串来关联待观察的字段,但是在swift中这么用总是觉得有点别扭。而我实际上想要的是这个样的:
kvo(obj.propName) {newValue in
callback
}
在现有的swift语法中是无法实现的,所以我想用个技巧来解决这个问题:
-
在obj实例上关联一个计数检查字段,假设是:checkNum
-
在kvo函数中,调用 autoclosure { obj.propName }。
-
每次访问propName的时候,执行atomicIncrement(CheckNum)
-
在autoclosure { obj.propName } 执行后,进行判断:
// 伪代码 while (true) { if CAS(oldCheckNum, checkNum, 1) == 1 { // 关联当前propName与callback } else { // 重置checkNum,并且重新调用propName } }
因为是针对每个实例对象进行检查,且是lock-free的,所以性能不会有问题。可惜的是Atomic系列API都已经deprecated,因此只能用GCD实现了。虽然性能差一些,但KVO行为本身就是异步的,也算是可以接受了。