sink()方法,而 sink 方法其实就是 signal.send(_:)方法,这里在闭包中捕获了signal 变量,于是就形成了循环引用。这里只要使用 weak 就能解决。修改下的代码是这样的:
static func empty() -> ((Result) -> Void, Signal) {
let signal = Signal()
return ({[weak signal] value in signal?.send(value)}, signal)
}
再次运行, Signal 的析构方法就能执行了。
上面就实现了一个简单的响应式编程的库了。不过这里还存在很多问题,比如我们应该在适当的时机移除观察者,现在我们的观察者被添加在 subscribers 数组中,这样就不知道该移除哪一个观察者,所以我们将数字替换成字典,用 UUID 作为 key :
fileprivate typealias Token = UUID
fileprivate var subscribers: [Token: Subscriber] = [:]
我们可以模仿 RxSwift 中的 Disposable 用来移除观察者,实现代码如下:
final class Disposable {
private let dispose: () -> Void
static func create(_ dispose: @escaping () -> Void) -> Disposable {
return Disposable(dispose)
}
init(_ dispose: @escaping () -> Void) {
self.dispose = dispose
}
deinit {
dispose()
}
}
原来的 subscribe(_:) 返回一个 Disposable 就可以了:
func subscribe(_ subscriber: @escaping (Result) -> Void) -> Disposable {
let token = UUID()
subscribers[token] = subscriber
return Disposable.create {
self.subscribers[token] = nil
}
}
这样我们只要在适当的时机销毁 Disposable 就可以移除观察者了。
作为一个响应式编程库都会有 map, flatMap, filter, reduce 等方法,所以我们的库也不能少,我们可以简单的实现几个。
map
map 比较简单,就是将一个 返回值为包装值的函数 作用于一个包装(Wrapped)值的过程, 这里的包装值可以理解为可以包含其他值的一种结构,例如 Swift 中的数组,可选类型都是包装值。它们都有重载的 map, flatMap等函数。以数组为例,我们经常这样使用:
let images = ["1", "2", "3"].map{ UIImage(named: $0) }
现在来实现我们的 map 函数:
func map(_ transform: @escaping (Value) -> T) -> Signal {
let (sink, signal) = Signal.empty()
let dispose = subscribe { (result) in
sink(result.map(transform))
}
signal.objects.append(dispose)
return signal
}
我同时给 Result 也实现了 map 函数:
extension Result {
func map(_ transform: @escaping (Value) -> T) -> Result {
switch self {
case .success(let value):
return .success(transform(value))
case .error(let error):
return .error(error)
}
}
}
// Test
let (sink, intSignal) = Signal.empty()
intSignal
.map{ String($0)}
.subscribe { result in
print(result)
}
sink(.success(100))
// Print success("100")








