你说“我没有选择器”是对的,因为这就是其中的一半。您可以使用组合接收来自通知中心的通知。
另一半是您可以将处理通知的逻辑推送到合并管道中,以便正确的结果会在管道的末端弹出(如果它到达您)。
老式的方式
假设我有一个卡片视图,当通过发布通知点击它时会发出虚拟的尖叫声:
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 操作符将其安全地转换为卡片(如果它不是卡片,管道就会停止,好像什么都没发生)。我们只想要卡片的name,所以我们使用map 运算符来获取它。结果如下:
let cardTappedCardNamePublisher =
NotificationCenter.default.publisher(for: Card.tapped)
.compactMap {$0.object as? Card}
.map {$0.name}
假设cardTappedCardNamePublisher 是我们视图控制器的一个实例属性。那么我们现在拥有的是一个实例属性,如果 Card 发布了tapped 通知,它会发布一个字符串,否则什么都不做。
当我说逻辑被推入管道时,你明白我的意思吗?
那么我们将如何安排接收来自管道末端的内容?我们可以使用水槽:
let sink = self.cardTappedCardNamePublisher.sink {
print($0) // the string name of a card
}
如果您尝试一下,您会发现我们现在的情况是,每次用户点击一张卡片时,都会打印出卡片的名称。这就是我们之前的 register-an-observer-with-a-selector 方法的 Combine 等价物。