Swift 超类和子类的协议扩展方法分派
Swift protocol extension method dispatch with superclass and subclass
我发现了一个有趣的行为,它看起来像是一个错误...
基于以下文章描述的行为:
https://medium.com/ios-os-x-development/swift-protocol-extension-method-dispatch-6a6bf270ba94
http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future
当我添加 SomeSuperclass
而不是直接采用协议时,输出不是我所期望的。
protocol TheProtocol {
func method1()
}
extension TheProtocol {
func method1() {
print("Called method1 from protocol extension")
}
func method2NotInProtocol() {
print("Called method2NotInProtocol from protocol extension")
}
}
// This is the difference - adding a superclass
class SomeSuperclass: TheProtocol {
}
// It works as expected when it simply adopts TheProtocol, but not when it inherits from a class that adopts the protocol
class MyClass: SomeSuperclass {
func method1() {
print("Called method1 from MyClass implementation")
}
func method2NotInProtocol() {
print("Called method2NotInProtocol from MyClass implementation")
}
}
let foo: TheProtocol = MyClass()
foo.method1() // expect "Called method1 from MyClass implementation", got "Called method1 from protocol extension"
foo.method2NotInProtocol() // Called method2NotInProtocol from protocol extension
您知道这是错误还是设计使然?一位同事建议,混合继承和协议扩展可能无法按预期工作。我打算使用协议扩展来提供默认实现...如果我做不到,那么我将遗憾地标记它 @objc
并返回到可选协议。
请检查以下代码:
import UIKit
protocol TheProtocol {
func method1()
func method2NotInProtocol()
}
extension NSObject {
func method1() {
print("Called method1 from protocol extension")
}
func method2NotInProtocol() {
print("Called method2NotInProtocol from protocol extension")
}
}
// This is the difference - adding a superclass
class SomeSuperclass :NSObject, TheProtocol {
override func method1() {
print("Called method1 from SomeSuperclass implementation")
}
}
// It works as expected when it simply adopts TheProtocol, but not when it inherits from a class that adopts the protocol
class MyClass : SomeSuperclass {
override func method1() {
print("Called method1 from MyClass implementation")
}
override func method2NotInProtocol() {
print("Called method2NotInProtocol from MyClass implementation")
}
}
let foo: TheProtocol = MyClass()
foo.method1() // expect "Called method1 from MyClass implementation", got "Called method1 from protocol extension"
foo.method2NotInProtocol() // Called method2NotInProtocol from protocol extension
与其将扩展写入 TheProtocol,不如将扩展写入抽象 class(上面代码中的 NSObject)。这按预期工作。
来自 post The Ghost of Swift Bugs Future,这里是 post.
末尾提到的协议扩展的调度规则
- 如果变量的推断类型是协议:
- AND 方法是在原始协议中定义的 然后
调用运行时类型的实现,无论是否
扩展中有一个默认实现。
- 并且该方法未在原始协议中定义,然后
调用默认实现。
- ELSE IF 变量的推断类型是该类型 THEN 调用该类型的实现。
所以在你的情况下,你是说 method1() 是在协议中定义的,并且已经在 subclass 中实现了。但是你的 superclass 正在采用协议但它没有实现 method1() 和 subclass 只是继承自 Superclass 而不是直接采用协议。这就是为什么我相信这就是你调用 foo.method1() 的原因,它不会调用第 1 点和第 2 点所述的 subclass 实现。
但是当你这样做时,
class SomeSuperclass: TheProtocol {
func method1(){
print("super class implementation of method1()")}
}
class MyClass : SomeSuperclass {
override func method1() {
print("Called method1 from MyClass implementation")
}
override func method2NotInProtocol() {
print("Called method2NotInProtocol from MyClass implementation")
}
}
然后当你打电话时,
let foo: TheProtocol = MyClass()
foo.method1() // Called method1 from MyClass implementation
foo.method2NotInProtocol()
所以这个错误(这似乎是一个错误)的解决方法是,你需要在 superclass 中实现协议方法,然后你需要重写协议方法子class。 HTH
一个尚未考虑的选项是将您的协议拆分为几个较小的协议,这样超类就不需要遵守包含它不打算实现的方法的协议。然后子类可以单独订阅这些其他协议。
我发现了一个有趣的行为,它看起来像是一个错误...
基于以下文章描述的行为:
https://medium.com/ios-os-x-development/swift-protocol-extension-method-dispatch-6a6bf270ba94
http://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future
当我添加 SomeSuperclass
而不是直接采用协议时,输出不是我所期望的。
protocol TheProtocol {
func method1()
}
extension TheProtocol {
func method1() {
print("Called method1 from protocol extension")
}
func method2NotInProtocol() {
print("Called method2NotInProtocol from protocol extension")
}
}
// This is the difference - adding a superclass
class SomeSuperclass: TheProtocol {
}
// It works as expected when it simply adopts TheProtocol, but not when it inherits from a class that adopts the protocol
class MyClass: SomeSuperclass {
func method1() {
print("Called method1 from MyClass implementation")
}
func method2NotInProtocol() {
print("Called method2NotInProtocol from MyClass implementation")
}
}
let foo: TheProtocol = MyClass()
foo.method1() // expect "Called method1 from MyClass implementation", got "Called method1 from protocol extension"
foo.method2NotInProtocol() // Called method2NotInProtocol from protocol extension
您知道这是错误还是设计使然?一位同事建议,混合继承和协议扩展可能无法按预期工作。我打算使用协议扩展来提供默认实现...如果我做不到,那么我将遗憾地标记它 @objc
并返回到可选协议。
请检查以下代码:
import UIKit
protocol TheProtocol {
func method1()
func method2NotInProtocol()
}
extension NSObject {
func method1() {
print("Called method1 from protocol extension")
}
func method2NotInProtocol() {
print("Called method2NotInProtocol from protocol extension")
}
}
// This is the difference - adding a superclass
class SomeSuperclass :NSObject, TheProtocol {
override func method1() {
print("Called method1 from SomeSuperclass implementation")
}
}
// It works as expected when it simply adopts TheProtocol, but not when it inherits from a class that adopts the protocol
class MyClass : SomeSuperclass {
override func method1() {
print("Called method1 from MyClass implementation")
}
override func method2NotInProtocol() {
print("Called method2NotInProtocol from MyClass implementation")
}
}
let foo: TheProtocol = MyClass()
foo.method1() // expect "Called method1 from MyClass implementation", got "Called method1 from protocol extension"
foo.method2NotInProtocol() // Called method2NotInProtocol from protocol extension
与其将扩展写入 TheProtocol,不如将扩展写入抽象 class(上面代码中的 NSObject)。这按预期工作。
来自 post The Ghost of Swift Bugs Future,这里是 post.
末尾提到的协议扩展的调度规则- 如果变量的推断类型是协议:
- AND 方法是在原始协议中定义的 然后 调用运行时类型的实现,无论是否 扩展中有一个默认实现。
- 并且该方法未在原始协议中定义,然后 调用默认实现。
- ELSE IF 变量的推断类型是该类型 THEN 调用该类型的实现。
所以在你的情况下,你是说 method1() 是在协议中定义的,并且已经在 subclass 中实现了。但是你的 superclass 正在采用协议但它没有实现 method1() 和 subclass 只是继承自 Superclass 而不是直接采用协议。这就是为什么我相信这就是你调用 foo.method1() 的原因,它不会调用第 1 点和第 2 点所述的 subclass 实现。
但是当你这样做时,
class SomeSuperclass: TheProtocol {
func method1(){
print("super class implementation of method1()")}
}
class MyClass : SomeSuperclass {
override func method1() {
print("Called method1 from MyClass implementation")
}
override func method2NotInProtocol() {
print("Called method2NotInProtocol from MyClass implementation")
}
}
然后当你打电话时,
let foo: TheProtocol = MyClass()
foo.method1() // Called method1 from MyClass implementation
foo.method2NotInProtocol()
所以这个错误(这似乎是一个错误)的解决方法是,你需要在 superclass 中实现协议方法,然后你需要重写协议方法子class。 HTH
一个尚未考虑的选项是将您的协议拆分为几个较小的协议,这样超类就不需要遵守包含它不打算实现的方法的协议。然后子类可以单独订阅这些其他协议。