问题 仅当符合特定协议时,才对类进行Swift扩展


嗨那里=)我刚刚面临一个设计问题,我需要(基本上)做以下事情:

我想注入一些代码 viewWillAppear: 的 任何 UIViewController 符合协议的子类 MyProtocol。在代码中解释:

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. 调用两者 MyProtocol 和 UIViewController 蜜蜂
  2. 覆盖 UIViewController 方法 initialize() 为了能够调酒 viewWillAppear:

这两个功能似乎不兼容(从Swift 3开始),因为:

  1. 我们 不能扩展有条件的类 (即 extension UIViewController where Self: MyProtocol
  2. 如果我们改为 扩展协议,我们可以添加条件 extension MyProtocol where Self: UIViewController 但 我们不能覆盖协议扩展中的类的方法,意思是我们不能 public override class func initialize() 这是调酒所需要的。

所以我想知道是否有人可以为我面临的这个问题提供Swifty解决方案? =)

提前致谢!!


9582
2017-11-17 12:44


起源

为什么不使用简单的继承?它是覆盖默认类方法的理想选择。 - Max Pevsner
嘿@MaxPevsner感谢您的评论! =)我选择不使用继承,因为我将它构建为要在多个项目中使用的系统的一部分,我不希望API强制每个项目的UIViewControllers从特定的超类继承('因为可能每个那些项目需要他们自己的基类)所以如果可能的话我想避免这样做。 - Robertibiris
我相信,你在这里错过了一点。但这种讨论远远超出了这个问题的范围。 - Max Pevsner


答案:


你接近解决方案。只需要另辟蹊径。仅在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
  }
}

8
2018-04-14 19:48



嘿谢谢你的回复=)这个问题就是我在描述中所说的: “如果我们改为扩展协议,我们可以添加条件扩展MyProtocol其中Self:UIViewController但是 我们不能覆盖协议扩展中的类的方法,这意味着我们不能公开覆盖调用所需的类func initialize()。“ - Robertibiris


好吧,到目前为止,我没有找到真正令人满意的方法,但我决定发布我最终为这个特定问题做的事情。 简而言之,解决方案就像这样(使用原始示例代码):

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方法并对ALL生效 UIViewControllers即使我们只验证符合的那些 MyProtocol 用于运行敏感代码行的协议。

我非常希望它可以帮助那些面临类似情况的人=)


4
2017-11-23 08:04