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

How to override swift protocol functions for subclasses (e.g. UILabel from UIView)

我正在尝试实现一个扩展功能,该功能应该根据 class 使用它的类型而有所不同。 对象需要是 UIView(或 subclass)。它应该始终使用在指定类型上扩展的函数,但如果它不符合其中任何一个,则应改用 UIView 方法(作为后备)。

这是我正在尝试做的一个例子:

protocol aProtocol {
    typealias completionBlock = (_ finished:Bool)->()
    func doSomething(completion: completionBlock)
}


extension UIView: aProtocol {
    func doSomething(completion: (Bool) -> ()) {
        print("Im an UIView")
    }
}

extension aProtocol where Self: UILabel {
    func doSomething(completion: (Bool) -> ()) {
        print("im an UILabel")
    }
}

extension aProtocol where Self: UIImageView {
    func doSomething(completion: (Bool) -> ()) {
        print("im an UIImageView")
    }
}

执行:

UIView().doSomething { (foo) in } // Should print "Im an UIView"
UIButton().doSomething { (foo) in } // Should print "Im an UIView" (UIButton doesent have specified extended function so should fall back on the UIView function)
UILabel().doSomething { (foo) in } // Should print "im an UILabel"
UIImageView().doSomething { (foo) in } // Should print "im an UIImageView"

现在打印:

Im an UIView
Im an UIView
Im an UIView
Im an UIView

这意味着它总是使用 UIView 方法,即使我希望它使用它自己的方法。我的目标是打印:

Im an UIView
Im an UIView
im an UILabel
im an UIImageView
protocol aProtocol {
    typealias completionBlock = (_ finished:Bool)->()
}

extension aProtocol {

    func doSomething(completion: completionBlock) {
        switch self {
        case is UILabel:
            print("im a label")
            break
        case is UIView:
            print("im a view")
            break
        default:
            break
        }

    }
}

extension UIView: aProtocol{}
extension UILabel: aProtocol{}

func testing() {

    let view = UIView()
    view.doSomething { (value) in
        // Do something
    }

    let label = UILabel()
    label.doSomething { (value) in
        // Do something
    }
}

确保 UIView 的大小写位于列表的底部(或至少在您要屏蔽的所有内容下方)

- 答案

符合协议的具体类型将用于协议约束。所以通过改变这个:

extension UIView: aProtocol {
    func doSomething(completion: (Bool) -> ()) {
        print("Im an UIView")
    }
}

对此:

extension aProtocol where Self: UIView {
    func doSomething(completion: (Bool) -> ()) {
        print("im an UIView")
    }
}

extension UIView: aProtocol {}

您的代码将按预期运行。

- 备选方案

您可以通过以下方式实现所有想要的打印效果:

extension aProtocol {
    func doSomething(completion: completionBlock) {
        print("im a \(type(of: self))")
    }
}

extension UIView: aProtocol {}

这意味着您可以在协议扩展中检查对象的实际类型。

- 解释

协议扩展不会覆盖任何方法。事实上,如果实际的具体类型没有实现它们,它们只是默认实现。

协议约束定义了类型可以推断它的默认实现。所以:

extension aProtocol where Self: UILabel

表示 UILabel 的任何符合 aProtocol 且未实现要求的子类应推断默认实现。所以这只有在 UILabel 直接符合 aProtocol 的情况下才有效:

extension UILabel: aProtocol {}

或者如果它的 supercalss 符合它:

extension UIView: aProtocol {}

您可以如下实现,您只需要将 aProtocol 暴露给 Objective-c 运行时,以便 overriding 它在 extension.

中的方法
@objc protocol aProtocol {
    typealias completionBlock = (_ finished:Bool)->()
    func doSomething(completion: completionBlock)
}

extension UIView: aProtocol {
    func doSomething(completion: (Bool) -> ()) {
        print("Im an UIView")
    }
}

extension UILabel {
    override func doSomething(completion: (Bool) -> ()) {
        // you can call super.doSomething(completion: completion)
        print("im an UILabel")
    }
}

extension UIImageView {
    override func doSomething(completion: (Bool) -> ()) {
        // you can call super.doSomething(completion: completion)
        print("im an UIImageView")
    }
}

输出:

Im an UIView
Im an UIView
im an UILabel
im an UIImageView