如何使用Selector和NotificationCenter调用可选功能

时间:2017-07-13 08:51:01

标签: ios swift selector swift-protocols

语言: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)
    }
}

如果我将此代码扩展为,请说UIViewController

class CrashingViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        self.listenToFoo()
    }
}

extension CrashingViewController: SomeProtocol { } 

现在出现了问题,因为foo是一个可选函数,如果任何人发送带有密钥Notification的{​​{1}},应用程序将崩溃,因为我没有实现{{ 1}}。所以在这种情况下,上面的代码会导致崩溃。

但是,如果我这样做

NSNotification.Name(rawValue: "UltimateNotificationKeyLOL")

由于foo不再是可选项,因此不会创建崩溃。

:此代码无法class GodzillaViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.listenToFoo() } } extension GodzillaViewController: SomeProtocol { func foo(_ notification: Notification) { print("lol") } }

问题:是否可以让选择器调用可选功能而不会使应用程序崩溃?

1 个答案:

答案 0 :(得分:4)

如果我在哪里,我会像这样制定一个完整的协议:

// 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)
        }
    }
}

正如您所看到的,这样做有很多好处:

  1. 你有一个只有Swift的代码(没有@objc)
  2. 通过添加foo的默认实现,您可以使该函数成为可选项。
  3. 您的通知仍然可以调用foo而不会崩溃,因为如果有必要,它会转到默认实现
  4. 如果您还想做某事,甚至可以在默认方法中添加一些代码!
  5. <强>更新

    你可以在listenToFoo()函数中看到我使用了另一个使用闭包的addObserver函数,原因是#selector仍然需要将函数暴露给@objc和闭包不:

      

    func addObserver(forName name:NSNotification.Name ?, object obj:Any ?, queue:OperationQueue ?, using block:@escaping(Notification) - &gt; void) - &gt; NSObjectProtocol