当某些协议函数更改其签名时,我可以强制编译器失败吗?

Can I force the compiler to fail when some protocol functions change their signature?

有没有办法将某些 Swift 函数标记为实现某些协议函数,以便如果协议的签名发生变化,编译器可以将实现标记为错误。

例如,考虑这个例子,我有一个 UIViewController 实例的 Foo 协议的默认实现,我想为自定义 Bar [=27] 覆盖它=] 是 UIViewController.

的子 class
// V1
protocol Foo {
    func number() -> Int
}

extension Foo where Self: UIViewController {
    func number() -> Int {
        return 0
    } 
}

// Overrides the default implementation
extension Bar: Foo {
    func number() -> Int {
        return 1
    }
}

现在,协议演变为:

// V2
protocol Foo {
    func numberV2() -> Int
}

extension Foo where Self: UIViewController {
    func numberV2() -> Int {
        return 0
    } 
}

// I think I override the default implementation but not anymore.
// => error prone, no error from the compiler
extension Bar: Foo {
    func number() -> Int {
        return 1
    }
}

如何帮助我的 Bar 分机意识到 number 函数不再与 Foo 协议相关?

不,目前 (Swift 4.0) 无法从编译器获得帮助来捕获这些 "near misses"。不过,这个话题已经在 swift-evolution 邮件列表中多次出现,所以人们肯定知道这是一个应该解决的问题。

事实上,Doug Gregor 最近 made a change 到 Swift 编译器,它将能够捕获一些 "near misses"。此更改不是当前 Swift 版本的一部分,但会在 Swift 4.1.

来自 the mailing list post 介绍更改:

A “near-miss” warning fires when there is a protocol conformance for which:

1) One of the requirements is satisfied by a “default” definition (e.g., one from a protocol extension), and

2) There is a member of the same nominal type declaration (or extension declaration) that declared the conformance that has the same name as the requirement and isn’t satisfying another requirement.

These are heuristics, of course, and we can tune the heuristics over time.

如果我理解正确,当前的启发式方法将无法捕捉到您的确切问题,因为它们只匹配具有完全相同名称(但类型不同)的方法,但正如 Doug 所说,未来可能会改进启发式方法。

如果功能将被覆盖,这就是默认协议实现应尽可能具体的原因之一。

如前所述,没有直接的方法可以做到这一点,但我会提供一些解决方法。

  • 有 2 个协议,一个有默认实现,一个没有。例如AutoFoo: FooFoo。在 AutoFoo 上具有默认实现,并且让 Bar 仅遵守 Foo,以免受到 AutoFoo 的默认实现的影响。
  • 更改名称时使用重构引擎(内置 Xcode 引擎 应该 工作)
  • 更改协议中的名称,构建,然后找到所有错误的地方。

您可以不定义管理类型 UIViewController 对象的协议扩展,而是提供一个 UIViewController 根 class 来实现您的 Foo 协议并对需要实现 Foo.

的所有其他 UIViewController 使用 subclasses

用代码解释:

您的 Foo 协议:

protocol Foo {
    func number() -> Int
}

下面是 Root class 的样子:

class Root: UIViewController, Foo {
    func number() -> Int {
        return 1
    }
}

以及您对 subclass 的重写实现:

class Bar: Root {
    override func number() -> Int {
        return 5
    }
}

每次更改 Foo 协议的方法签名时,编译器都会针对 Root class 抛出错误,因为它不再符合 Foo 协议并反对 Bar subclass(一旦你修复了这个错误)因为它不再覆盖 Root 的任何方法。

对于不需要重写 Foo 的 classes,您可以从 Root 继承,它将使用默认实现:

class Joe: Root {
}

完整代码(之前):

protocol Foo {
    func number() -> Int
}

extension Foo where Self: UIViewController {
    func number() -> Int {
        return 0
    }
}

// Class with an overrided implementation of the number() method.
class Bar: UIViewController, Foo {
  func number() -> Int {
      return 1
  }
}

// Class uses the default implementation of the number() method.
class Joe: UIViewController, Foo {

}

完整代码(之后):

protocol Foo {
    func number() -> Int
}

class Root: UIViewController, Foo {
    func number() -> Int {
        return 1
    }
}

// Class with an overrided implementation of the number() method.
class Bar: Root {
    override func number() -> Int {
        return 5
    }
}

// Class uses the default implementation of the number() method.
class Joe: Root {
}

希望对您有所帮助。