如何使用 Selector 和 NotificationCenter 调用可选函数
How to call an optional function using Selector and NotificationCenter
语言:Swift 3
IDE: XCode 8.3.2 (8E2002)
我有一个带有可选功能的协议foo
@objc protocol SomeProtocol {
@objc optional func foo(_ notification: Notification)
}
extension SomeProtocol {
func listenToFoo() {
NotificationCenter.default.addObserver(self, selector: #selector(self.foo(_:)), name: NSNotification.Name(rawValue: "UltimateNotificationKeyLOL"), object: nil)
}
}
如果我将此代码扩展为 class,比如 UIViewController
。
class CrashingViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.listenToFoo()
}
}
extension CrashingViewController: SomeProtocol { }
现在问题来了,因为 foo
是一个可选函数,如果任何人发送一个 Notification
键 NSNotification.Name(rawValue: "UltimateNotificationKeyLOL")
应用程序将崩溃,因为我没有实现foo
还没有。所以在这种情况下,上面的代码会导致崩溃。
但是如果我这样做
class GodzillaViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.listenToFoo()
}
}
extension GodzillaViewController: SomeProtocol {
func foo(_ notification: Notification) {
print("lol")
}
}
没有崩溃,因为 foo(_:)
不再是可选的。
也:此代码不可用#selector(self.foo?(_:))
问题:是否可以让选择器调用可选函数而不使应用程序崩溃?
如果我在你那里,我会像这样制定一个完整的swift协议:
// Protocol declaration
protocol SomeProtocol {
func foo(_ notification: Notification)
}
// Provide default implementation for optional methods of SomeProtocol
extension SomeProtocol {
func foo(_ notification: Notification) {}
}
// Extend SomeProtocol with additional methods
extension SomeProtocol {
func listenToFoo() {
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "UltimateNotificationKeyLOL"), object: nil, queue: nil) { (notification) in
self.foo(notification)
}
}
}
如您所见,这样做有很多好处:
- 您只有 Swift 代码(没有 @objc)
- 通过添加
foo
的默认实现,您将函数设为可选。
- 您的通知仍然可以调用
foo
而不会崩溃,因为它会在必要时转到默认实现
- 如果您仍然想做一些事情,您甚至可以在默认方法中添加一些代码!
更新
你可以在 listenToFoo()
函数中看到我使用了一个不同的 addObserver
函数,它使用闭包来代替,原因是 #selector 仍然需要将函数暴露给 @objc 和闭包没有:
func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol
语言:Swift 3
IDE: XCode 8.3.2 (8E2002)
我有一个带有可选功能的协议foo
@objc protocol SomeProtocol {
@objc optional func foo(_ notification: Notification)
}
extension SomeProtocol {
func listenToFoo() {
NotificationCenter.default.addObserver(self, selector: #selector(self.foo(_:)), name: NSNotification.Name(rawValue: "UltimateNotificationKeyLOL"), object: nil)
}
}
如果我将此代码扩展为 class,比如 UIViewController
。
class CrashingViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.listenToFoo()
}
}
extension CrashingViewController: SomeProtocol { }
现在问题来了,因为 foo
是一个可选函数,如果任何人发送一个 Notification
键 NSNotification.Name(rawValue: "UltimateNotificationKeyLOL")
应用程序将崩溃,因为我没有实现foo
还没有。所以在这种情况下,上面的代码会导致崩溃。
但是如果我这样做
class GodzillaViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.listenToFoo()
}
}
extension GodzillaViewController: SomeProtocol {
func foo(_ notification: Notification) {
print("lol")
}
}
没有崩溃,因为 foo(_:)
不再是可选的。
也:此代码不可用#selector(self.foo?(_:))
问题:是否可以让选择器调用可选函数而不使应用程序崩溃?
如果我在你那里,我会像这样制定一个完整的swift协议:
// Protocol declaration
protocol SomeProtocol {
func foo(_ notification: Notification)
}
// Provide default implementation for optional methods of SomeProtocol
extension SomeProtocol {
func foo(_ notification: Notification) {}
}
// Extend SomeProtocol with additional methods
extension SomeProtocol {
func listenToFoo() {
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "UltimateNotificationKeyLOL"), object: nil, queue: nil) { (notification) in
self.foo(notification)
}
}
}
如您所见,这样做有很多好处:
- 您只有 Swift 代码(没有 @objc)
- 通过添加
foo
的默认实现,您将函数设为可选。 - 您的通知仍然可以调用
foo
而不会崩溃,因为它会在必要时转到默认实现 - 如果您仍然想做一些事情,您甚至可以在默认方法中添加一些代码!
更新
你可以在 listenToFoo()
函数中看到我使用了一个不同的 addObserver
函数,它使用闭包来代替,原因是 #selector 仍然需要将函数暴露给 @objc 和闭包没有:
func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol