合并:从带有选择器的通知中心 addObserver 到通知发布者
Combine: Going from Notification Center addObserver with selector to Notification publisher
我已经看到如何从一些 NotificationCenter
代码中使用 Publisher 过渡到 Combine,但还没有看到如何为类似的东西做到这一点:
NotificationCenter.default.addObserver(
self,
selector: #selector(notCombine),
name: NSNotification.Name(rawValue: "notCombine"),
object: nil
)
我已经看到它可以作为发布者使用,但是我没有selector
并且我不确定该怎么做:
NotificationCenter.default.publisher(
for: Notification.Name(rawValue: "notCombine")
)
有人知道吗?谢谢!
用例并不完全清楚,但这里有一个基础游乐场示例:
import Combine
import Foundation
class CombineNotificationSender {
var message : String
init(_ messageToSend: String) {
message = messageToSend
}
static let combineNotification = Notification.Name("CombineNotification")
}
class CombineNotificationReceiver {
var cancelSet: Set<AnyCancellable> = []
init() {
NotificationCenter.default.publisher(for: CombineNotificationSender.combineNotification)
.compactMap{[=10=].object as? CombineNotificationSender}
.map{[=10=].message}
.sink() {
[weak self] message in
self?.handleNotification(message)
}
.store(in: &cancelSet)
}
func handleNotification(_ message: String) {
print(message)
}
}
let receiver = CombineNotificationReceiver()
let sender = CombineNotificationSender("Message from sender")
NotificationCenter.default.post(name: CombineNotificationSender.combineNotification, object: sender)
sender.message = "Another message from sender"
NotificationCenter.default.post(name: CombineNotificationSender.combineNotification, object: sender)
对于某些用例,您还可以将其设为仅组合解决方案而不使用通知
import Combine
import Foundation
class CombineMessageSender {
@Published var message : String?
}
class CombineMessageReceiver {
private var cancelSet: Set<AnyCancellable> = []
init(_ publisher: AnyPublisher<String?, Never>) {
publisher
.compactMap{[=11=]}
.sink() {
self.handleNotification([=11=])
}
.store(in: &cancelSet)
}
func handleNotification(_ message: String) {
print(message)
}
}
let sender = CombineMessageSender()
let receiver = CombineMessageReceiver(sender.$message.eraseToAnyPublisher())
sender.message = "Message from sender"
sender.message = "Another message from sender"
你说 "I don't have a selector" 是对的,因为那是正确的一半。您可以使用 Combine 在没有选择器的情况下接收来自通知中心的通知。
要点的另一半是,您可以将处理通知的逻辑推送到 Combine 管道中,以便正确的结果在到达您时直接从管道的末端弹出。
老式的方式
假设我有一个卡片视图,当它通过发布通知被点击时会发出虚拟的尖叫声:
static let tapped = Notification.Name("tapped")
@objc func tapped() {
NotificationCenter.default.post(name: Self.tapped, object: self)
}
现在让我们假设,为了示例的目的,游戏在收到这些通知之一时感兴趣的是发布的卡片的 name
属性 的字符串值通知。如果我们以老式方式执行此操作,那么获取该信息是一个两个阶段的过程。首先,我们必须注册才能接收通知:
NotificationCenter.default.addObserver(self,
selector: #selector(cardTapped), name: Card.tapped, object: nil)
然后,当我们收到通知时,我们必须查看它的 object
是否真的是一张卡片,如果是,则获取它的 name
属性 并执行一些东西:
@objc func cardTapped(_ n:Notification) {
if let card = n.object as? Card {
let name = card.name
print(name) // or something
}
}
合并方式
现在让我们使用 Combine 框架做同样的事情。我们通过调用其 publisher
方法从通知中心获取发布者。但我们不止于此。如果 object
不是卡片,我们不想收到通知,因此我们使用 compactMap
运算符将其安全地转换为卡片(如果不是卡片,管道就像什么都没发生一样停下来)。我们只想要 Card 的 name
,所以我们使用 map
运算符来获取它。结果如下:
let cardTappedCardNamePublisher =
NotificationCenter.default.publisher(for: Card.tapped)
.compactMap {[=13=].object as? Card}
.map {[=13=].name}
假设 cardTappedCardNamePublisher
是我们视图控制器的一个实例 属性。那么我们现在拥有的是一个实例 属性,如果 Card 发布 tapped
通知,它会发布一个字符串,否则什么都不做。
当我说逻辑被推入管道时,你明白我的意思吗?
那么我们如何安排接收来自管道末端的内容呢?我们可以使用水槽:
let sink = self.cardTappedCardNamePublisher.sink {
print([=14=]) // the string name of a card
}
如果您尝试一下,您会发现我们现在遇到的情况是,每次用户点击卡片时,都会打印卡片的名称。这就是我们之前使用选择器注册观察者方法的 Combine 等价物。
我已经看到如何从一些 NotificationCenter
代码中使用 Publisher 过渡到 Combine,但还没有看到如何为类似的东西做到这一点:
NotificationCenter.default.addObserver(
self,
selector: #selector(notCombine),
name: NSNotification.Name(rawValue: "notCombine"),
object: nil
)
我已经看到它可以作为发布者使用,但是我没有selector
并且我不确定该怎么做:
NotificationCenter.default.publisher(
for: Notification.Name(rawValue: "notCombine")
)
有人知道吗?谢谢!
用例并不完全清楚,但这里有一个基础游乐场示例:
import Combine
import Foundation
class CombineNotificationSender {
var message : String
init(_ messageToSend: String) {
message = messageToSend
}
static let combineNotification = Notification.Name("CombineNotification")
}
class CombineNotificationReceiver {
var cancelSet: Set<AnyCancellable> = []
init() {
NotificationCenter.default.publisher(for: CombineNotificationSender.combineNotification)
.compactMap{[=10=].object as? CombineNotificationSender}
.map{[=10=].message}
.sink() {
[weak self] message in
self?.handleNotification(message)
}
.store(in: &cancelSet)
}
func handleNotification(_ message: String) {
print(message)
}
}
let receiver = CombineNotificationReceiver()
let sender = CombineNotificationSender("Message from sender")
NotificationCenter.default.post(name: CombineNotificationSender.combineNotification, object: sender)
sender.message = "Another message from sender"
NotificationCenter.default.post(name: CombineNotificationSender.combineNotification, object: sender)
对于某些用例,您还可以将其设为仅组合解决方案而不使用通知
import Combine
import Foundation
class CombineMessageSender {
@Published var message : String?
}
class CombineMessageReceiver {
private var cancelSet: Set<AnyCancellable> = []
init(_ publisher: AnyPublisher<String?, Never>) {
publisher
.compactMap{[=11=]}
.sink() {
self.handleNotification([=11=])
}
.store(in: &cancelSet)
}
func handleNotification(_ message: String) {
print(message)
}
}
let sender = CombineMessageSender()
let receiver = CombineMessageReceiver(sender.$message.eraseToAnyPublisher())
sender.message = "Message from sender"
sender.message = "Another message from sender"
你说 "I don't have a selector" 是对的,因为那是正确的一半。您可以使用 Combine 在没有选择器的情况下接收来自通知中心的通知。
要点的另一半是,您可以将处理通知的逻辑推送到 Combine 管道中,以便正确的结果在到达您时直接从管道的末端弹出。
老式的方式
假设我有一个卡片视图,当它通过发布通知被点击时会发出虚拟的尖叫声:
static let tapped = Notification.Name("tapped")
@objc func tapped() {
NotificationCenter.default.post(name: Self.tapped, object: self)
}
现在让我们假设,为了示例的目的,游戏在收到这些通知之一时感兴趣的是发布的卡片的 name
属性 的字符串值通知。如果我们以老式方式执行此操作,那么获取该信息是一个两个阶段的过程。首先,我们必须注册才能接收通知:
NotificationCenter.default.addObserver(self,
selector: #selector(cardTapped), name: Card.tapped, object: nil)
然后,当我们收到通知时,我们必须查看它的 object
是否真的是一张卡片,如果是,则获取它的 name
属性 并执行一些东西:
@objc func cardTapped(_ n:Notification) {
if let card = n.object as? Card {
let name = card.name
print(name) // or something
}
}
合并方式
现在让我们使用 Combine 框架做同样的事情。我们通过调用其 publisher
方法从通知中心获取发布者。但我们不止于此。如果 object
不是卡片,我们不想收到通知,因此我们使用 compactMap
运算符将其安全地转换为卡片(如果不是卡片,管道就像什么都没发生一样停下来)。我们只想要 Card 的 name
,所以我们使用 map
运算符来获取它。结果如下:
let cardTappedCardNamePublisher =
NotificationCenter.default.publisher(for: Card.tapped)
.compactMap {[=13=].object as? Card}
.map {[=13=].name}
假设 cardTappedCardNamePublisher
是我们视图控制器的一个实例 属性。那么我们现在拥有的是一个实例 属性,如果 Card 发布 tapped
通知,它会发布一个字符串,否则什么都不做。
当我说逻辑被推入管道时,你明白我的意思吗?
那么我们如何安排接收来自管道末端的内容呢?我们可以使用水槽:
let sink = self.cardTappedCardNamePublisher.sink {
print([=14=]) // the string name of a card
}
如果您尝试一下,您会发现我们现在遇到的情况是,每次用户点击卡片时,都会打印卡片的名称。这就是我们之前使用选择器注册观察者方法的 Combine 等价物。