美文网首页
从实现角度看ReativeX

从实现角度看ReativeX

作者: 码农苍耳 | 来源:发表于2017-07-11 23:09 被阅读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也比较简单,将执行放到对应的队列中即可。

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

相关文章

  • 从实现角度看ReativeX

    有很多在给人介绍Reactive的几个开源项目(ReactiveCocoa, RxSwift)的使用,我就不想在这...

  • 从应用角度看ReativeX

    这篇从我个人的角度聊聊ReactiveX这种思想的应用场景。 首先不论MVVM的使用场景,我也没有特别丰富的MVV...

  • unity的c#脚本和标准的c#语言有什么区别和联系?unity

    这个问题可以从Unity支持的.NET标准和实现细节两个角度看。 ​从对 .NET标准支持的角度看,这只是一个版本...

  • 从源码角度看Golang的TCP Socket(epoll)实现

    从源码角度看Golang的TCP Socket(epoll)实现 Golang的TCP是基于系统的epoll IO...

  • 从写手角度看网文写作/ 写作探索

    从写手角度看网文写作 / 写作探索 上一章 从读者角度看网文写作 / 写作探索 昨日写了从读者角度看网文写作,今...

  • Android IPC基础速成

    Binder是Android中的一个类,它实现了IBinder接口。从IPC角度看,Binder是Android中...

  • 从契约角度看婚姻

    古往今来,人们都想弄明白婚姻是怎么回事。古代婚姻是父母之命,媒妁之言。今天人们更多的自由恋爱,自由选择。相...

  • 从价值角度看Ulord

    几天前偶然接触到了ULord,阅读了几遍白皮书,以下从“价值”角度与大家做个分享。 传统互联网传递的是信息,而区块...

  • 从NLP角度看算命

    NLP课堂上同学们对算命话题讨论热烈,马老师也作了相应解答。刚刚在读《做自己的NLP教练》时,有一个词叫感觉敏锐性...

  • 从哲学角度看欲望

    格非教授《欲望的旗帜》读完了,这部属于他早期创作的长篇小说,让我再一次被他大气磅礴的文学造诣和匠心独运的巧妙构思...

网友评论

      本文标题:从实现角度看ReativeX

      本文链接:https://www.haomeiwen.com/subject/tyrvhxtx.html