在swift中实现KVO的思路

2016-12-02  本文已影响82人  fuadam1982

我们知道如果不借助NSObject基类是无法实现oc中的KVO的。而KVO的基本原理是很简单的,把每次对属性的get、set转发给订阅者就可以了。但是如果没有编译器和runtime的支持仅使用swift的语法,实际�使用起来还是很不方便的(其实同样的问题在java、c#中早已有之)。一个简单的解决方案是定义一个observable接口去桥接属性的get\set方法与该类外部的订阅者(参考windows phone做MVVM的方式)。

我目前想到的折中解决方案是这样的:

  1. 利用上篇提到的"关联对象"将对一个属性的get/set 转发到自定义的KVO属性对象上

  2. 在类的外部利用属性名字符串进行订阅,但为了类型安全代码写起来有点复杂(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语法中是无法实现的,所以我想用个技巧来解决这个问题:

  1. 在obj实例上关联一个计数检查字段,假设是:checkNum

  2. 在kvo函数中,调用 autoclosure { obj.propName }。

  3. 每次访问propName的时候,执行atomicIncrement(CheckNum)

  4. 在autoclosure { obj.propName } 执行后,进行判断:

     // 伪代码
     while (true) {
         if CAS(oldCheckNum, checkNum, 1) == 1 {
             // 关联当前propName与callback
         } else {
             // 重置checkNum,并且重新调用propName
          }
     }
    

因为是针对每个实例对象进行检查,且是lock-free的,所以性能不会有问题。可惜的是Atomic系列API都已经deprecated,因此只能用GCD实现了。虽然性能差一些,但KVO行为本身就是异步的,也算是可以接受了。

上一篇下一篇

猜你喜欢

热点阅读