Objective-C任择协议方法的默认实现

时间:2016-03-26 11:55:59

标签: swift protocols

如何为Objective-C可选协议方法提供默认实现?

实施例

extension AVSpeechSynthesizerDelegate {
  func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) {
    print(">>> did finish")
  }
}

期望:无论何时符合AVSpeechSynthesizerDelegate的类都应该在语音完成时运行上述函数。

1 个答案:

答案 0 :(得分:3)

你完全按照你的实施方式去做。差异最终在于如何实际调用该方法。

让我们以非常简化示例:

@objc protocol FooProtocol {
    optional func bar() -> Int
}

class Omitted: NSObject, FooProtocol {}
class Implemented: NSObject, FooProtocol {
    func bar() -> Int {
        print("did custom bar")
        return 1
    }
}

通过不添加其他代码,我希望必须使用此代码:

let o: FooProtocol = Omitted()
let oN = o.bar?()

let i: FooProtocol = Implemented()
let iN = i.bar?()

oNiN最终都有Int?类型,oNniliN1且我们看到文字"did custom bar"打印。

重要的是,不是可选链接的方法调用:bar?(),括号中方法名称之间的问号。这就是我们必须从Swift调用可选协议方法的方法。

现在让我们为我们的协议添加扩展名:

extension FooProtocol {
    func bar() -> Int {
        print("did bar")
        return 0
    }
}

如果我们坚持原始代码,我们可选择链接方法调用,行为没有变化:

enter image description here

然而,通过协议扩展,我们不再需要选择性地解包。我们可以选择展开,扩展名称为:

enter image description here

这里不幸的问题是,这不一定特别有用,是吗?现在我们每次只调用扩展中实现的方法。

所以如果你可以控制使用协议和调用方法的类,那么选择就会更好一些。您可以检查班级是否响应选择器:

let i: FooProtocol = Implemented()

if i.respondsToSelector("bar") {
    i.bar?()
}
else {
    i.bar()
}

enter image description here

这也意味着您必须修改协议声明:

@objc protocol FooProtocol: NSObjectProtocol

添加NSObjectProtocol可以让我们拨打respondsToSelector,并且根本不会改变我们的协议。我们必须继承NSObject才能实施标记为@objc的协议。

当然,尽管如此,任何Objective-C代码都无法在Swift类型上执行此逻辑,并且可能无法实际调用这些协议中实现的方法似乎是扩展。因此,如果您试图从Apple的框架中获取某些内容来调用扩展方法,那么您似乎运气不好。似乎即使你试图在Swift中调用其中一个,如果它的协议方法标记为optional,那么这不是一个非常好的解决方案。