3

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

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

// 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协议相关?

4

3 回答 3

6

不,目前(Swift 4.0)没有办法从编译器那里获得帮助来捕捉这些“未遂事件”。不过,这个话题已经在 swift-evolution 邮件列表中多次出现,所以人们肯定知道这是一个应该解决的问题。

事实上,Doug Gregor 最近对 Swift 编译器进行了更改,可以捕获一些“未遂事件”。此更改不是当前 Swift 版本的一部分,但将在 Swift 4.1 中。

从介绍更改的邮件列表帖子中:

当存在以下协议一致性时,会触发“near-miss”警告:

1) “默认”定义(例如,来自协议扩展的一个)满足其中一项要求,并且

2) 有一个相同名义类型声明(或扩展声明)的成员声明了与需求具有相同名称且不满足另一个需求的一致性。

当然,这些是启发式方法,我们可以随着时间的推移调整启发式方法。

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

于 2017-11-27T23:29:18.927 回答
2

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

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

  • 有 2 个协议,一个有默认实现,一个没有。例如 AutoFoo: FooFoo。将默认实现放在 上AutoFoo,并且只遵守 BarFoo以免受到AutoFoo默认实现的影响。
  • 更改名称时使用重构引擎(内置的 Xcode 引擎应该可以工作)
  • 更改协议中的名称,构建,然后找到所有有错误的地方。
于 2017-11-27T23:49:38.547 回答
0

除了定义管理 type 对象的协议扩展之外,您UIViewController还可以提供一个UIViewController根类来实现您的Foo协议并为所有其他UIViewController需要实现的对象使用子类Foo

用代码解释:

你的Foo协议:

protocol Foo {
    func number() -> Int
}

下面是这个Root类的样子:

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

以及您重写的子类实现:

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

每次更改Foo协议的方法签名时,编译器都会针对类抛出错误,Root因为它不再符合Foo协议和Bar子类(一旦您修复了此错误),因为它不会覆盖任何方法Root了。

对于不需要覆盖的类,Foo您可以继承自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 {
}

希望能帮助到你。

于 2017-11-29T09:59:50.263 回答