仅在符合特定协议时才对类进行 Swift 扩展
Posted
技术标签:
【中文标题】仅在符合特定协议时才对类进行 Swift 扩展【英文标题】:Swift extension of a class ONLY when it conforms to a specific protocol 【发布时间】:2016-11-17 12:44:05 【问题描述】:您好 =) 我刚刚遇到了一个设计问题,我需要(基本上)执行以下操作:
我想在 任何符合协议 MyProtocol
的 UIViewController
子类的 viewWillAppear:
上注入一些代码。代码解释:
protocol MyProtocol
func protocolFunction()
//do cool stuff...
extension UIViewController where Self: MyProtocol //<-----compilation error
public override class func initialize()
//swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool)
// MARK: - Swizzling
func xxx_viewWillAppear(animated: Bool)
self.xxx_viewWillAppear(animated)
//invoke APIs from
self.protocolFunction() // MyProtocol APIs
let viewLoaded = self.isViewLoaded // UIViewController APIs
这里的主要问题是我需要在 UIVIewController
扩展中做两件事:
-
同时调用
MyProtocol
和 UIViewController
API
覆盖UIViewController
方法initialize()
以便能够调动viewWillAppear:
这 2 个功能似乎不兼容(从 Swift 3 开始),因为:
-
我们不能用条件扩展类(即
extension UIViewController where Self: MyProtocol
)
如果我们改为扩展协议,我们可以添加条件extension MyProtocol where Self: UIViewController
,但我们不能覆盖协议扩展中类的方法,这意味着我们可以' t public override class func initialize()
这是调酒所需要的。
所以我想知道是否有人可以为我面临的这个问题提供 Swifty 解决方案? =)
提前致谢!!
【问题讨论】:
为什么不使用简单继承?它是覆盖默认类方法的理想选择。 嘿@MaxPevsner 感谢您的评论! =) 我选择不使用继承,因为我将其构建为系统的一部分,以便在多个项目中使用,并且我不希望 API 强制每个项目的 UIViewControllers 从特定的超类继承(因为可能每个这些项目需要自己的基类)所以如果可能的话我想避免这种情况。 我相信,你在这里漏掉了一点。但这种讨论远远超出了这个问题的范围。 【参考方案1】:您已接近解决方案。只需要换一种方式。仅当协议是 UIViewController 的一部分时才扩展协议。
protocol MyProtocol
func protocolFunction()
//do cool stuff...
extension MyProtocol where Self: UIViewController
public override class func initialize()
//swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool)
// MARK: - Swizzling
func xxx_viewWillAppear(animated: Bool)
self.xxx_viewWillAppear(animated)
//invoke APIs from
self.protocolFunction() // MyProtocol APIs
let viewLoaded = self.isViewLoaded // UIViewController APIs
【讨论】:
嘿,感谢您的回复 =) 问题是我在描述中所说的:"如果我们改为扩展协议,我们可以添加条件扩展 MyProtocol where Self: UIViewController but 我们不能覆盖协议扩展中类的方法,这意味着我们不能公开覆盖 swizzling 所需的类 func initialize()。”【参考方案2】:嗯,到目前为止,我还没有找到真正令人满意的方法,但我决定发布我最终为这个特定问题所做的事情。 简而言之,解决方案是这样的(使用原始示例代码):
protocol MyProtocol
func protocolFunction()
//do cool stuff...
extension UIViewController //------->simple extension on UIViewController directly
public override class func initialize()
//swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool)
// MARK: - Swizzling
func xxx_viewWillAppear(animated: Bool)
self.xxx_viewWillAppear(animated)
//------->only run when self conforms to MyProtocol
if let protocolConformingSelf = self as? MyProtocol
//invoke APIs from
protocolConformingSelf.protocolFunction() // MyProtocol APIs
let viewLoaded = protocolConformingSelf.isViewLoaded // UIViewController APIs
缺点:
不是“快速”的做事方式 将调用 swizzling 方法并对所有UIViewControllers
生效,即使我们仅验证符合 MyProtocol
协议的那些以运行敏感代码行。
我非常希望它可以帮助其他面临类似情况的人 =)
【讨论】:
以上是关于仅在符合特定协议时才对类进行 Swift 扩展的主要内容,如果未能解决你的问题,请参考以下文章