DeliciousPubSub 1.1.0

DeliciousPubSub 1.1.0

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布最后发布2017 年 9 月
SwiftSwift 版本3.0
SPM支持 SPM

Skye things维护。



  • 作者
  • Skylark

Delicious PubSub

Swift 强类型 PubSub 库。美味又有营养,我的父母就不会再对我失望了。

概述

使用 Delicious PubSub,您可以按类型订阅消息……任何类型!它使用 Swift 相对有限的反射 API,通过类型的名称将类型映射到已注册的回调……因此它实际上只是管理一个包含字符串到函数的字典。

有几种注册订阅者(当我完成写作后,下面将有很多例子),但它们都回到了这个函数: sub<T: Any>(fn: T -> Void) -> Void -> Void

sub 接收一个闭包,该闭包接受一个您订阅的消息类型的参数。这个闭包被添加为该消息类型的处理程序,当消息被分发时,会调用这个闭包实例。

sub 返回一个取消订阅的函数,它只是从 PubSub 对象的处理程序列表中删除注册的处理程序。当您想要您的处理程序停止处理分发的消息时,只需调用对应 sub 调用返回的函数即可。

发布消息只有一种方式。使用 pub(message: Any).

还有一个公开函数 dispatchMessages(),稍后将会解释……

当 PubSub 对象被析构时,它会移除所有的处理程序。这释放了对闭包的引用。

用法

按类订阅消息

此示例演示了如何按类订阅消息,同时也展示了如何取消订阅,尽管在这个示例中您不需要这样做。

class SecretMessage {
    let secret: String
    init(secret: String) {
        self.secret = secret
    }
}

let pubSub = PubSub()

let unsubscribe = pubSub.sub(SecretMessage.self) {
    print("Wow. A secret message. It says '\($0.secret)'...");
}

pubSub.pub(SecretMessage(secret: "I hate you"))

unsubscribe()

按原始类型订阅消息

您可以按任何类型订阅消息,这意味着您理论上可以使用原始类型,如 Double、Int、Bool、String……我不是说您应该这样做,但您确实可以……只是确保没有人在看着。这是我生活中大部分事情的做法。

let pubSub = PubSub()

let _ = pubSub.sub { (int: Int) in
    print("\(int) is the loneliest number...")
}

pubSub.pub(1)

有点奇怪,对吧。

基于条件的订阅…

您可以使用预条件来控制订阅回调的执行。我不知道,也许您只关注以字母“S”开头的秘密信息。

let pubSub = PubSub()

let _ = pubSub.sub(
    SecretMessage.self,
    predicate: {
        $0.secret.hasPrefix("S")
    },
    fn: {
        print("'\($0.secret)' starts with S and I like that. A lot...")
    })

pubSub.pub(SecretMessage(secret: "ABC"))
pubSub.pub(SecretMessage(secret: "123"))
pubSub.pub(SecretMessage(secret: "@(*&$@)#($&*#@"))
pubSub.pub(SecretMessage(secret: "Spongey old grapes!"))
pubSub.pub(SecretMessage(secret: "sludge magnet"))

一次性订阅

您可以使用subOnce函数向消息添加一次性订阅。回调将只调用一次,然后立即取消订阅。就像sub一样,subOnce也返回一个取消订阅的函数。您可以通过调用它来提前取消您的订阅,并防止处理器运行。

let pubSub = PubSub()

let actuallyNoIChangedMyMind = pubSub.subOnce(String.self) { _ in
    fatalError("This will never be called!")
}

actuallyNoIChangedMyMind()

pubSub.pub("RAAARRRRR..... I hate myself.")

在基于条件的订阅中,您只能监听并响应满足预条件的消息的第一个出现,然后再也不必考虑它。

let pubSub = PubSub()

let _ = pubSub.subOnce(
    Int.self,
    predicate: { $0 <= 1 },
    fn: { _ in print("Hooray.") })

pubSub.pub(3)
pubSub.pub(2)
pubSub.pub(1)
pubSub.pub(0)

消息分发

您可能已经注意到,您可以带或不带dispatchImmediately参数初始化PubSub对象。

dispatchImmediately的默认值为true。这是做什么的?

如果dispatchImmediately为true,则任何已发布的消息将在返回之前立即分配给该消息类型的所有订阅者。

然而,如果dispatchImmediately为false,则发布的消息将不会分配,直到您明确地在您的PubSub对象上调用dispatchMessages()

在大多数情况下,您可能不必担心这一点,因此可以使用默认行为。如果您发现自己想要累积发布的消息,并控制它们何时被处理,那么请进行如下操作:let pubSub = PubSub(dispatchImmediately: false),然后在您准备就绪时执行pubSub.dispatchMessages()

在手动分发的过程中,在发布消息后加入的订阅者将在最后分发时看到这些消息。这是需要考虑的事情。

let pubSub = PubSub(dispatchImmediately: false)

pubSub.pub(1)
pubSub.pub(2)

let _ = pubSub.sub { (int: Int) in
    print("Yay, the number \(int)! How exciting.")
}

pubSub.pub(3)

pubSub.dispatchMessages()

您应该明白了。

这么多PubSub

每个PubSub对象管理自己的订阅者,因此您可以根据需要拥有任意多的PubSub对象;因为没有共享的全局状态,所以您的订阅不会冲突。这可能是显而易见的。

let pubSub1 = PubSub()
let unsub1 = pubSub1.sub { (int: Int) in print("pubSub1 got \(int)") }

let pubSub2 = PubSub()
let unsub2 = pubSub2.sub { (int: Int) in print("pubSub2 got \(int)") }

pubSub1.pub(1)
pubSub2.pub(2)

unsub1()

pubSub1.pub(1)
pubSub2.pub(2)

unsub2()

pubSub1.pub(1)
pubSub2.pub(2)

性能

这里有处理没有执行动作的处理器(即空函数体)时处理一个消息类型的统计数据。

将10,000条消息每条发送到1000个订阅者:约7.933秒
将10,000条消息每条发送到100个订阅者:约1.155秒
将100条消息每条发送到100个订阅者:约0.011秒
将100条消息每条发送到10个订阅者:约0.002秒

如果您在处理具有长时间运行或潜在操作的处理器,则可以在处理器中使用GCD。通过GCD分发会产生其自身的开销。

订阅过载和类型推断。

您会注意到每个函数都有一个带有type: T.Type参数来添加订阅者的重载。

这完全是出于样式上的考虑。在Swift的类型推断中,它在每种情况下都是完全不必要的,但每个人都有自己关于什么看起来好或不好的看法,所以,呃,你想做什么就做什么。

let pubSub = PubSub()

let _ = pubSub.sub(String.self) {
        print($0)
}

let _ = pubSub.sub { (string: String) in
        print(string)
}

let _ = pubSub.sub(
        String.self,
        predicate: { $0.hasPrefix("A") },
        fn: { print($0) })

let _ = pubSub.sub(
        predicate: { (string: String) in string.hasPrefix("A") },
        fn: { print($0) })

取消订阅的一些说明

我不知道该把它放在哪里,所以这就是为什么它在这里,在结尾。

您可以对取消订阅函数进行多次调用。它只会执行一次。

您可以在另一个处理程序内部调用取消订阅函数。如果该处理程序在您想注销的处理程序调用之前运行,则它将正常工作... 下面是一个例子...

let pubSub = PubSub()

var unsub: (Void -> Void)!

let _ = pubSub.sub { (_: Int) in
    unsub()
}

unsub = pubSub.sub { (_: Int) in
    fatalError("This should never happen.")
}

pubSub.pub(1)

unsub()

这就是全部了。

如果您不喜欢它,请告诉我为什么。

如果您想改进它,请提交一个PR。我相信您会比我能做得更好。