【问题标题】:How to override swift protocol functions for subclasses (e.g. UILabel from UIView)如何覆盖子类的 swift 协议函数(例如 UIView 中的 UILabel)
【发布时间】:2019-08-05 11:38:53
【问题描述】:

我正在尝试实现一个扩展函数,该函数应该根据使用它的类的类型而有所不同。 对象需要是 UIView(或子类)。它应该始终使用在指定类型上扩展的函数,但如果它不符合其中任何一个,则应该使用 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

【问题讨论】:

  • 因为一个UILabel永远是一个UIView,
  • @vollan 当然,UILabel 永远是 UIView 的子类。我想弄清楚的是子类的指定函数是否可以覆盖 UIView 函数。
  • 抱歉,误会了。在下面发布答案。
  • @Pointblaster 查看我的答案。我解释了为什么会发生这种情况以及如何满足您的需求。
  • 问题是扩展没有创建覆盖。他们定义了一种新方法。

标签: ios swift uiview protocols


【解决方案1】:

您可以按如下方式实现,您只需将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

【讨论】:

  • 这确实既有趣又奇怪。你能解释一下为什么它可以使用@objc 协议吗?
  • 这正是我想要的。谢谢!
【解决方案2】:

- 答案

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

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 {}

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

- 解释

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

并且协议约束定义了witch type可以推断出它的默认实现。所以:

extension aProtocol where Self: UILabel

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

extension UILabel: aProtocol {}

或者如果它的超类符合它:

extension UIView: aProtocol {}

【讨论】:

    【解决方案3】:
    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 的大小写在列表的底部(或至少在您想要屏蔽的所有内容的下方)

    【讨论】:

      猜你喜欢
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      • 2015-06-26
      • 2017-04-01
      • 1970-01-01
      • 2015-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多