当一个类符合特定协议时,如何编写Swift扩展?

24

嗨 =)我刚遇到一个设计问题,需要(本质上)执行以下操作:

我想在任何符合协议MyProtocolUIViewController子类的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扩展中做两件事情:
  1. 调用MyProtocolUIViewController API
  2. 覆盖UIViewController方法initialize(),以便能够交换viewWillAppear:
这两个功能在Swift 3中似乎不兼容,因为:
  1. 我们不能使用条件来扩展类(即extension UIViewController where Self: MyProtocol
  2. 如果我们扩展协议,我们可以添加条件extension MyProtocol where Self: UIViewController,但我们不能在协议扩展中覆盖来自类的方法,这意味着我们无法public override class func initialize()进行交换所需的操作。
因此,我想知道是否有人能够提供一种Swifty的解决方案来解决我面临的问题?=)
谢谢您的帮助!

为什么不使用简单的继承?它非常适合覆盖默认类方法。 - Max Pevsner
嘿@MaxPevsner,感谢您的评论! =)我选择不使用继承,因为我正在构建这个系统作为多个项目中使用的一部分,并且我不希望API强制每个项目的UIViewControllers从特定的超类继承(因为也许每个项目都需要自己的基类),所以如果可能的话,我想避免这种情况。 - Robertibiris
我相信你在这里忽略了一个要点。但是那个讨论远超出了这个问题的范围。 - Max Pevsner
2个回答

16

您已经接近解决方案了。只需要反过来做。仅在其为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
  }
}

3
嘿,谢谢回复 =) 问题在于我在描述中所说的:"如果我们扩展协议,我们可以添加条件扩展MyProtocol where Self: UIViewController,但是我们无法覆盖协议扩展中类的方法,这意味着我们无法公开覆盖class func initialize(),而这对于交换方法是必需的。" - Robertibiris

7

目前为止,我还没有找到一个真正令人满意的做法,但是我决定发布我为这个特定问题所做的解决方案。 简而言之,解决方案如下(使用原始示例代码):

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
        }

    }

}

缺点:

  • 不是“Swift方式”完成任务
  • 尽管我们验证了只有符合MyProtocol协议的UIViewControllers才运行敏感代码,但交换方法将被调用并影响所有UIViewControllers

我非常希望它能帮助其他人解决类似的情况 =)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接