从实现角度看ReativeX

2017-07-11  本文已影响17人  码农苍耳

有很多在给人介绍Reactive的几个开源项目(ReactiveCocoa, RxSwift)的使用,我就不想在这个方面写什么了。我是一个实践主义者,所以我从我的角度来谈谈这种方案:解决了什么样的问题,怎么实现的,以及适合应用的场景。同时也加深自己对Reactive的理解。

这里我们来看看是怎么实现的,关于pure functionmonad的部分我也不准备介绍了,毕竟我在这方面还不是特别熟悉。

signal observer

首先我们来想一下一个最简单信号的流程。当一个信号被订阅时(subscribe),发出信号后会触发订阅者(observer)执行下一步(sendNext)。那么一个最简单的信号(signal)和订阅者(observer)的协议就如下。

protocol Observer {
    func send(next: AnyObject)
    func send(error: NSError)
    func sendComplete()
}

protocol Signal {
    func subscribe(_ observer: �Observer)
}

那么现在实现一个最简单的UIButton的信号。

class ButtonSignal: Signal {
    var observer: Observer?

    init(button: UIButton) {
        button .addTarget(self, action: #selector(onButton(sender:)), for: .touchUpInside)
    }

    @objc func onButton(sender: UIButton) {
        self.observer?.send(next: sender)
    }

    func subscribe(_ observer: Observer) {
        self.observer = observer
    }
}

class ButtonSignal: Signal {
    var observer: Observer?

    init(button: UIButton) {
        button .addTarget(self, action: #selector(onButton(sender:)), for: .touchUpInside)
    }

    func onButton(sender: UIButton) {
        self.observer?.send(next: sender)
    }

    func subscribe(_ observer: Observer) {
        self.observer = observer
    }
}

然后实现一个最简单的订阅者。

class ButtonObserver: Observer {
    func send(next: AnyObject) {
        print("send next!")
    }
    func sendComplete() {
        print("send complete")
    }
    func send(error: NSError) {
        print("send \(error)")
    }
}

最后连接起来

signal = ButtonSignal(button: button)
signal?.subscribe(ButtonObserver())

那么问题来了,难道我们要为每个信号都创建一个类吗。当然不是,我们可以创建一个通用的信号和订阅者。

class BlockSignal: Signal {
    typealias CreateBlock = (Observer)->Void

    var block: CreateBlock

    init(block:@escaping CreateBlock) {
        self.block = block
    }

    func subscribe(_ observer: Observer) {
        self.block(observer)
    }
}

class BlockObserver: Observer {
    var next: (AnyObject)->Void
    var complete: ()->Void
    var error: (NSError)->Void

    init(next: @escaping (AnyObject)->Void,
         complete: @escaping ()->Void,
         error: @escaping (NSError)->Void) {
        self.next = next
        self.complete = complete
        self.error = error
    }

    @objc func send(next: AnyObject) {
        self.next(next)
    }

    func sendComplete() {
        self.complete()
    }

    func send(error: NSError) {
        self.error(error)
    }
}

同时在使用的过程时通过block来创建具体信号。

signal = BlockSignal(block: { (observer) in
    self.button.addTarget(observer,
                          action: #selector(BlockObserver.send(next:)),
                          for: .touchUpInside)
})

observer = BlockObserver(next: { (sender) in
    print("send next!")
}, complete: {
    print("send complete")
}) { (error) in
    print("send error")
}
signal?.subscribe(observer!)

以上就是最简单的信号量和订阅者实现。这里为了简洁的说明问题,所以没有考虑到内存方面的问题。

dispose

上节说了内存方面的问题。还有一个问题就是如何取消订阅呢。那么这里需要有模块负责释放(dispose)。

那么将接口改为

protocol Disposable {
    func dispose()
}

protocol Signal {
    func subscribe(_ observer: Observer) -> Disposable
}

实现也按照block形式

class BlockDisposable: Disposable {
    var block: ()->Void

    init(block: @escaping ()->Void) {
        self.block = block
    }

    func dispose() {
        self.block()
    }
}

class BlockSignal: Signal {
    func subscribe(_ observer: Observer) -> Disposable {
        return self.block(observer)
    }
}

使用时和上面基本一致

signal = BlockSignal(block: { (observer) in
    self.button.addTarget(observer,
                          action: #selector(BlockObserver.send(next:)),
                          for: .touchUpInside)
    return BlockDisposable(block: {
        self.button.removeTarget(observer,
                                 action: #selector(BlockObserver.send(next:)),
                                 for: .touchUpInside)
    })
})

需要解除订阅的时候

self.disposable = signal?.subscribe(observer!)
self.disposable?.dispose()

之后

到目前为止,可以说signal-observer部分已经完全实现了。

其中冷信号和热信号也非常简单

protocol Subject: Observer, Signal {}

Scheduler也比较简单,将执行放到对应的队列中即可。

下篇结合我的角度来聊聊应用场景。

上一篇 下一篇

猜你喜欢

热点阅读