如何在Swift的Protocol中定义可选方法(译)

7,031 阅读2分钟

使用默认实现(推荐)

protocol MyProtocol {
    func doSomething()
}

extension MyProtocol {
    func doSomething() {
        /* return a default value or just leave empty */
    }
}

struct MyStruct: MyProtocol {
    /* no compile error */
}

优势

  • 没有牵扯到OC的runtime(至少没有明显的用到)。这就意味着你的structs、enum、或者非继承自NSObject的class去遵守它。此外,你还可以使用功能强大的泛型(generics) 。对泛型不了解的,可以通过这篇文章来了解泛型的基本使用。
  • 当遇到符合此类protocol的类型时,你可以确保满足所有要求。它始终是具体实现或默认实现,这就相当于其他语言的“interfaces”或“contracts”的作用。

劣势

  • 对于有返回值的函数,你需要返回一个合适的默认值,这并不总是可行的。但是,当你遇到了这个问题,就说明这个函数不应该声明为可选的,或者你在设计API的时候犯错了。
  • 你无法区分是提供了一个默认的实现,还是并没有实现。至少没有一个特殊的返回值来解决该问题。看一下下面的代码:
protocol SomeParserDelegate {
    func validate(value: Any) -> Bool
}

如果你提供了一个返回值为true的默认实现,乍一看这么做没有问题。接下来考虑一下下面的伪代码:

final class SomeParser {
    func parse(data: Data) -> [Any] {
        if /* delegate.validate(value:) is not implemented */ {
            /* parse very fast without validating */
        } else {
            /* parse and validate every value */
        }
    }
}

这时是无法实现优化这个分支(if)的,因为你无法知道你的delegate是否实现了这个方法。 虽然有很多方法可以解决这个问题(使用optional closures,不同的delegate对象用于不同的操作等等),这个例子只是清晰地说明这么做存在的问题。

使用@objc optional

@objc protocol MyProtocol {
    @objc optional func doSomething()
}

class MyClass: NSObject, MyProtocol {
    /* no compile error */
}

优势

  • 不需要默认实现,通过@objc optional 关键字修饰就可以了。

劣势

  • 这样只能是符合OC中遵守protocol的类型,才能遵守该protocol,这样严重限制了Protocol的功能。这就意味着只有继承自NSObject的类才能遵守该协议。struct、enum都不行,也不能使用associated type。
  • 你必须始终通过可选地调用或检查符合类型是否实现它来检查是否实现了可选方法。如果您经常调用可选方法,这可能会引入很多冗余代码。

原文链接