static func empty() -> ((Result) -> Void, Signal) {
let signal = Signal()
return (signal.send, signal)
}
fileprivate func send(_ result: Result) { ... }
现在我们需要这样使用 Signal 了:
let (sink, signal) = Signal.empty()
signal.subscribe { result in
print(result)
}
sink(.success(100))
sink(.success(200))
接着我们可以给 UITextField 绑定一个 Signal,只需要在 Extension 中给 UITextField添加一个计算属性 :
extension UITextField {
var signal: Signal {
let (sink, signal) = Signal.empty()
let observer = KeyValueObserver(object: self, keyPath: #keyPath(text)) { str in
sink(.success(str))
}
signal.objects.append(observer)
return signal
}
}
上面代码中的 observer 是一个局部变量,在 signal调用完后,就会被销毁,所以需要在 Signal 中保存该对象,可以给 Signal 添加一个数组,用来保存需要延长生命周期的对象。 KeyValueObserver 是对 KVO 的简单封装,其实现如下:
final class KeyValueObserver: NSObject {
private let object: NSObject
private let keyPath: String
private let callback: (T) -> Void
init(object: NSObject, keyPath: String, callback: @escaping (T) -> Void) {
self.object = object
self.keyPath = keyPath
self.callback = callback
super.init()
object.addObserver(self, forKeyPath: keyPath, options: [.new], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath, keyPath == self.keyPath, let value = change?[.newKey] as? T else { return }
callback(value)
}
deinit {
object.removeObserver(self, forKeyPath: keyPath)
}
}
现在就可以使用textField.signal.subscribe({}) 来观察 UITextField 内容的改变了。
在 Playground 写个 VC 测试一下:
class VC {
let textField = UITextField()
var signal: Signal?
func viewDidLoad() {
signal = textField.signal
signal?.subscribe({ result in
print(result)
})
textField.text = "1234567"
}
deinit {
print("Removing vc")
}
}
var vc: VC? = VC()
vc?.viewDidLoad()
vc = nil
// Print
success("1234567")
Removing vc
Reference Cycles
我在上面的 Signal 中,添加了 deinit方法:
deinit {
print("Removing Signal")
}
最后发现 Signal 的析构方法并没有执行,也就是说上面的代码中出现了循环引用,其实仔细分析上面 UITextField 的拓展中 signal的实现就能发现问题出在哪儿了。
let observer = KeyValueObserver(object: self, keyPath: #keyPath(text)) { str in
sink(.success(str))
}
在 KeyValueObserver 的回调中,调用了








