利用属性观察器实现数据绑定
数据绑定的多种实现方法

想法
- 在
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)
参考
下起雨,也要勇敢前行