如何覆盖子类的 swift 协议函数(例如 UIView 中的 UILabel)
Posted
技术标签:
【中文标题】如何覆盖子类的 swift 协议函数(例如 UIView 中的 UILabel)【英文标题】:How to override swift protocol functions for subclasses (e.g. UILabel from UIView) 【发布时间】: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 查看我的答案。我解释了为什么会发生这种情况以及如何满足您的需求。 问题是扩展没有创建覆盖。他们定义了一种新方法。 【参考方案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 的大小写在列表的底部(或至少在您想要屏蔽的所有内容的下方)
【讨论】:
以上是关于如何覆盖子类的 swift 协议函数(例如 UIView 中的 UILabel)的主要内容,如果未能解决你的问题,请参考以下文章
移动时如何访问 SKSpriteNode 子类的对象(Swift)