Swift协议扩展覆盖

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift协议扩展覆盖相关的知识,希望对你有一定的参考价值。

我正在尝试Swift协议扩展,我发现这个令人困惑的行为。你能帮助我如何得到我想要的结果吗?

请参阅代码最后4行的注释。 (如果需要,可以将其粘贴到Xcode7 playground)。谢谢!!

//: Playground - noun: a place where people can play

import UIKit

protocol Color { }
extension Color {  var color : String { return "Default color" } }

protocol RedColor: Color { }
extension RedColor { var color : String { return "Red color" } }


protocol PrintColor {

     func getColor() -> String
}

extension PrintColor where Self: Color {

    func getColor() -> String {

        return color
    }
}


class A: Color, PrintColor { }
class B: A, RedColor { }


let colorA = A().color // is "Default color" - OK
let colorB = B().color // is "Red color" - OK


let a = A().getColor() // is "Default color" - OK
let b = B().getColor() // is "Default color" BUT I want it to be "Red color"
答案

简短的回答是协议扩展不会进行类多态。这是有道理的,因为一个协议可以被struct或enum采用,并且因为我们不希望仅仅采用协议来引入动态调度而不需要它。

因此,在getColor()中,color实例变量(可能更准确地写为self.color)并不意味着你认为它的作用,因为你在思考类多态而协议不是。这样可行:

let colorB = B().color // is "Red color" - OK

...因为你要求一个班级解决color,但这不符合你的期望:

let b = B().getColor() // is "Default color" BUT I want it to be "Red color"

...因为getColor方法完全在协议扩展中定义。您可以通过在B中重新定义getColor来解决问题:

class B: A, RedColor {
    func getColor() -> String {
        return self.color
    }
}

现在这个类的getColor被称为,它具有self是什么的多态性概念。

另一答案

我设法通过在color上定义Color并切换B的实现列表来实现它。如果B必须是A,那就不好了。

protocol Color {
    var color : String { get }
}

protocol RedColor: Color {

}

extension Color {
    var color : String {
        get {return "Default color"}
    }
}

extension RedColor {
    var color : String {
        get {return "Red color"}
    }
}

protocol PrintColor {
    func getColor() -> String
}

extension PrintColor where Self: Color {
    func getColor() -> String {
        return color
    }
}

class A : Color, PrintColor {

}

class B : RedColor, PrintColor {

}

let a = A().getColor() // "Default color"
let b = B().getColor() // "Red color"
另一答案

这里有两个非常不同的问题:协议的动态行为和协议“默认”实现的解决方案。

  1. 在动态方面,我们可以通过一个简单的例子说明问题: protocol Color { } extension Color { var color: String { return "Default color" } } class BlueBerry: Color { var color: String { return "Blue color" } } let berry = BlueBerry() print("(berry.color)") // prints "Blue color", as expected let colorfulThing: Color = BlueBerry() print("(colorfulThing.color)") // prints "Default color"! 正如您在your answer中指出的那样,如果将color定义为原始Color协议的一部分,则可以获得动态行为(即,从而指示编译器合理地期望符合类实现此方法,并且如果没有找到则仅使用协议的实现): protocol Color { var color: String { get } } ... let colorfulThing: Color = BlueBerry() print("(colorfulThing.color)") // now prints "Blue color", as expected
  2. 现在,在your answer,你质疑为什么当BA的子类时这有点分崩离析。 我认为有必要记住协议扩展中的方法实现是“默认”实现,即如果符合类本身没有实现它,则使用实现。在你的情况下混淆的根源来自B符合RedColor的事实,color具有B的默认实现,但A也是Color的子类,其符合color,其具有http://codereview.stackexchange.com的不同默认实现。 所以,我们可能会对Swift对这种情况的处理提出质疑(我个人更愿意看到关于这种固有模糊情况的警告),但在我看来,问题的根源在于有两种不同的层次结构(OOP对象层次结构)子类和协议继承的POP协议层次结构)这导致两个竞争的“默认”实现。

我知道这是一个老问题,所以你可能很久以后就转向了其他事情,这很好。但是,如果您仍在努力寻找重构此代码的正确方法,请分享一下此类层次结构以及此协议继承实际代表的内容,我们可以提供更具体的建议。这是抽象例子进一步混淆问题的案例之一。让我们看看类型/协议到底是什么。 (如果你有工作代码,typealias MyFunction = () -> () protocol OptionalMethod { func optionalMethod() -> MyFunction? func executeOptionalMethod() } extension OptionalMethod { func optionalMethod() -> MyFunction? { return nil } func executeOptionalMethod() { if let myFunc = self.optionalMethod() { myFunc() } else { print("Type (self) has not implemented `optionalMethod`") } } } class A: OptionalMethod { } class B: A { func optionalMethod() -> MyFunction? { return { print("Hello optional method") } } } struct C: OptionalMethod { func optionalMethod() -> MyFunction? { return { print("Hello optionalMethod") } } } class D: OptionalMethod { func optionalMethod() -> MyFunction? { return { print("Hello optionalMethod") } } } class E: D { override func optionalMethod() -> MyFunction? { return { print("Hello DIFFERENT optionalMethod") } } } /* Attempt to get B to declare its own conformance gives: // error: redundant conformance of 'B2' to protocol 'OptionalMethod' class B2: A, OptionalMethod { func optionalMethod() -> MyFunction? { return { print("Hello optional method") } } } */ class A2: OptionalMethod { func optionalMethod() -> MyFunction? { return nil } } class B2: A2 { override func optionalMethod() -> MyFunction? { return { print("Hello optionalMethod") } } } let a = A() // Class A doesn't implement & therefore defaults to protocol extension implementation a.executeOptionalMethod() // Type __lldb_expr_201.A has not implemented `optionalMethod` let b = B() // Class B implements its own, but "inherits" implementation from superclass A b.executeOptionalMethod() // Type __lldb_expr_205.B has not implemented `optionalMethod` let c = C() // Struct C implements its own, and works c.executeOptionalMethod() // Hello optionalMethod let d = D() // Class D implements its own, inherits from nothing, and works d.executeOptionalMethod() // Hello optionalMethod let e = E() // Class E inherits from D, but overrides, and works e.executeOptionalMethod() // Hello DIFFERENT optionalMethod let a2 = A2() // Class A2 implements the method, but returns nil, (equivalent to A) a2.executeOptionalMethod() // Type __lldb_expr_334.A2 has not implemented `optionalMethod` let b2 = B2() // Class B2 overrides A2's "nil" implementation, and so works b2.executeOptionalMethod() // Hello optionalMethod 可能是更好的场所。)

另一答案

我在尝试通过协议实现“可选”方法时遇到了这个问题。它可以在结构中,在不继承的类中工作,也可以在从实现可以被覆盖的非协议默认方法的基础继承的类中工作。唯一不起作用的情况是继承自声明符合性但不提供其自身“非默认”实现的基类的类 - 在这种情况下,协议扩展的默认值是“烘焙”到基类,并且不能被覆盖或重新定义。

简单的例子:

color
另一答案

注意:建议的解决方案“将Color定义为原始RedBerry协议的一部分”并不能解决问题,例如当你有继承时,例如BlueBerry继承自Color,符合协议protocol Color { var color: String { get } } extension Color { var color: String { return "Default color" } } class BlueBerry: Color { // var color: String { return "Blue color" } } class RedBerry: BlueBerry { var color: String { return "Red color" } } let berry = RedBerry() print(berry.color) // Red color let colorfulThing: Color = RedBerry() print(colorfulThing.color) // Actual: Default color, Expected: Red color

qazxswpoi

以上是关于Swift协议扩展覆盖的主要内容,如果未能解决你的问题,请参考以下文章

如何覆盖子类的 swift 协议函数(例如 UIView 中的 UILabel)

协议方法是不是意味着在 Swift 中被覆盖?

覆盖在swift中的扩展对象内不起作用

覆盖在swift中的扩展对象内不起作用

Swift 协议扩展中的“关联类型”难以理解

仅在符合特定协议时才对类进行 Swift 扩展