swift - 只有在可以调用另一个方法的情况下,如何使一个方法出现在“重载集”中?
问题描述
考虑 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))
可以调用的情况下才会出现在重载集中。
那么,完成同样事情的快速方法是什么?
解决方案
您可以使用的一个技巧是重新组织您的代码。与其在 中创建多个(通用)addObserver
方法NotificationCenter
,不如将它们移动到您的通知类型(基本和自定义通知)中,并使用协议将它们形式化。然后,您可以使用单个 func 扩展此协议以添加addOnce
逻辑。当您的基本和自定义通知实现此协议时,它们会自动继承此新addOnce
功能,而无需任何重复代码。
例子
下面是一个关于如何实现这个想法的例子:
首先创建一个ObservableNotification
允许将观察者添加block
到NotificationCenter
.
protocol ObservableNotification {
static func addObserver(to center: NotificationCenter, using block: @escaping (Self)->Void) -> NotificationToken
}
然后,让你的 Notification 协议继承自这个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)
}
}
这样,您可以ObservableNotification
使用该方法的默认实现来扩展协议,并且observeOnce
您将能够在符合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
}
}
推荐阅读
- python - 我正在努力使用 tkinter 关闭一个简单的小部件
- python - Django user.save() 在列“is_superuser”上失败,不能为空
- elixir - 为什么我的 Enum.reduce 基于 Enum.all 的实现?返回一个空列表?
- python - 用星号屏蔽 SSN 的前五位数字
- c - 在 C 中创建字符串并将其传递给函数的最佳方法是什么?
- python - 尽管存在 Mac,Python pandas read_csv 返回 FileNotFoundError
- sql - postgresql - 将类似查询“分组”为子查询
- ember.js - 为什么 Ember Octane 中的跟踪阵列不会更新?
- codeigniter-3 - 如何从库类中调用辅助函数
- python - Folium Search Plugin 没有 FeatureGroup 的结果