Рассмотрим расширение в 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
}
Приведенный выше код в основном означает, что функция observeOnce
появляется только в наборе перегрузки, если можно вызвать addObserver(std::move(block))
.
Итак, каков быстрый способ сделать то же самое?