利用属性观察器实现数据绑定

数据绑定的多种实现方法

数据绑定实现方法

想法

  • didSet 方法里,通知观察者
  • 支持多个观察者
  • 支持取下监听
  • 无需显式取消监听,在销毁时自动取消监听

代码

public final class Box<T> {
    public typealias Listenr = (T) -> Void

    private var observers: NSHashTable<AnyObject>
    private var managerKey: Void?

    /// 真正存储的值
    public var value: T {
        didSet {
            observers.allObjects.forEach { (observer) in
                let block = objc_getAssociatedObject(observer, &managerKey)
                if let block1 = block as? Listenr {
                    block1(value)
                }
            }
        }
    }

    /// 初始化方法
    ///
    /// - Parameter value: 用来被绑定的value
    public init(_ value: T) {
        self.value = value
        observers = NSHashTable<AnyObject>.weakObjects()
    }


    /// 添加观察者。target在销毁时,会自动移除观察,不会有内存泄漏。
    ///
    /// - Parameters:
    ///   - target: 观察者,变化时,会通知到它
    ///   - block: 发生变化时,需要执行的操作。block为nil,取消绑定
    public func bind(target: AnyObject, block: Listenr?) {
        observers.add(target)
        objc_setAssociatedObject(target, &managerKey, block, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        block?(value)
        if block == nil {
            observers.remove(target)
        }
    }
}

几点说明

  • 使用了关联对象,把对监听对象的响应作为observer的一个属性。这样子,在observer销毁时,自然会取消监听
  • 使用了weak NSHashTable 来存储observers
  • 在初次绑定时,通知了observer当前值

使用例子

        let box = Box<Int>.init(4)
        let observer2 = ObserverExample.init()
        let observer3 = ObserverExample.init()
        box.bind(target: observer2) { [unowned observer2 ] (value) in
            observer2.value = value
        }
        box.bind(target: observer3) { [unowned observer3 ] (value) in
            observer3.value = value
        }
        
        // 去掉 observer2 对box 的监听
        box.bind(target: observer2, block: nil)
        box.value = -9
        XCTAssertEqual(box.value, observer3.value)
        XCTAssertNotEqual(box.value, observer2.value)

参考

posted on 2019-01-19 09:32  花老🐯  阅读(151)  评论(0编辑  收藏  举报

导航