考虑一下NotificationCenter上的一个扩展,它为符合某些协议的通知对象做了一些特殊的事情(是的,这些协议可能更常见,但这是一个帮助演示我真正的问题的示例)。
extension NotificationCenter {
func addObserver<Note: BasicNotification>(using block: @escaping (Note) -> ())
-> NotificationToken {
let observer = addObserver(forName: Note.notificationName, object: nil,
queue: nil, using: { note in
block(Note(notification: note))
})
return NotificationToken(observer: observer, center: self)
}
func addObserver<Note: CustomNotification>(using block: @escaping (Note) -> ())
-> NotificationToken {
let observer = addObserver(forName: Note.notificationName, object: nil,
queue: nil, using: { note in
block(note.object as! Note)
})
return NotificationToken(observer: observer, center: self)
}
}现在,考虑我们希望此通知只触发一次,然后取消注册...
extension NotificationCenter {
func observeOnce<Note: BasicNotification>(using block: @escaping (Note) -> ()) {
var token: NotificationToken!
token = addObserver(using: { (note: Note) in
block(note)
token.reset()
})
}
func observeOnce<Note: CustomNotification>(using block: @escaping (Note) -> ()) {
var token: NotificationToken!
token = addObserver(using: { (note: Note) in
block(note)
token.reset()
})
}
}这是完全相同的代码。我真正想要的是一个observeOnce方法--我不想写两个。
如果我不使用任何条件一致性...
func observeOnce<Note>(using block: @escaping (Note) -> ()) {
var token: NotificationToken!
token = addObserver(using: { (note: Note) in
block(note)
token.reset()
})
}我得到了一个错误Cannot invoke 'addObserver' with an argument list of type '(using: (Note) -> ())',这是非常有意义的。
如果我使用一个通用的基础协议(两者都符合)...
func observeOnce<Note: SomeNotification>(using block: @escaping (Note) -> ()) {
var token: NotificationToken!
token = addObserver(using: { (note: Note) in
block(note)
token.reset()
})
}我得到了完全相同的错误,这没有太多意义-我希望调用是模棱两可的,而不是根本不存在。
在看到&操作符意味着符合多个协议之后,我确实尝试过在极不可能的情况下使用BasicNotification | CommonNotification,因为这可能有一些意义……但它当然不起作用。
我还尝试了一堆其他的选择,但都没有用。我正在尝试做的是,如果其他任何一个可以调用,observeOnce都可以调用。
在C++中,我会这样做(没有通过编译器运行它--希望您能理解我想要做的事情)……
template <typename T>
auto observeOnce(std::function<void (T)> block)
-> decltype(void(this->addObserver(std::move(block))))
{
// do my stuff here
}上面的代码基本上意味着,如果可以调用addObserver(std::move(block)),则仅在重载集中出现函数observeOnce。
那么,完成同样的事情的快速方法是什么呢?
发布于 2018-07-11 22:52:05
你可以使用的一个技巧是重组你的代码。不是在NotificationCenter中创建多个(通用) addObserver方法,而是将它们移动到您的通知类型(基本通知和自定义通知)中,并使用协议将它们形式化。然后,您可以使用单个函数来扩展此协议,以添加addOnce逻辑。当您的基本通知和自定义通知实现此协议时,它们会自动继承此新的addOnce功能,而不需要任何重复的代码。

示例
这里有一个关于如何实现这个想法的例子:
首先,创建一个允许将观察者block添加到NotificationCenter的新协议ObservableNotification。
protocol ObservableNotification {
static func addObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken
}然后,让您的通知协议继承此ObservableNotification协议
protocol NameableNotification {
static var notificationName: NSNotification.Name {get}
}
protocol CustomNotification: NameableNotification, ObservableNotification {}
protocol BasicNotification: NameableNotification, ObservableNotification {
init(notification: Notification)
}并使用协议扩展将您的addObserver方法(从NotificationCenter)移动到相应的协议作为默认实现:
extension BasicNotification {
static func addObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken {
let observer = center.addObserver(forName: Self.notificationName, object: nil, queue: nil) { note in
block(Self(notification: note))
}
return NotificationToken(observer: observer, center: center)
}
}
extension CustomNotification {
static func addObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken {
let observer = center.addObserver(forName: Self.notificationName, object: nil, queue: nil) { note in
block(note.object as! Self)
}
return NotificationToken(observer: observer, center: center)
}
}这样,您可以使用observeOnce方法的默认实现来扩展ObservableNotification协议,并且能够在符合ObservableNotification的每个类型上调用它(在本例中为CustomNotification和BasicNotification )。如下所示:
extension ObservableNotification {
static func addOneTimeObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken {
var token: NotificationToken!
token = addObserver(to: center) {
block($0)
token.reset()
}
return token
}
}https://stackoverflow.com/questions/51194950
复制相似问题