ios - это - экземпляр протокола swift




Быстрое расширение класса ТОЛЬКО, когда оно соответствует конкретному протоколу (2)

Привет там =) Я просто столкнулся с проблемой проектирования, где мне нужно (по существу) сделать следующее:

Я хочу добавить немного кода в 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. UIViewController API MyProtocol и UIViewController API
  2. Переопределите метод UIViewController initialize() , чтобы иметь возможность swizzle viewWillAppear:

Эти 2 возможности кажутся несовместимыми (с Swift 3), потому что:

  1. Мы не можем расширять классы с условиями (например, extension UIViewController where Self: MyProtocol )
  2. если мы вместо этого расширим протокол , мы можем добавить extension MyProtocol where Self: UIViewController условий extension MyProtocol where Self: UIViewController но мы НЕ МОЖЕМ переопределять методы из класса в расширении протокола , то есть мы не можем public override class func initialize() который необходим для swizzling.

Поэтому мне было интересно, есть ли там кто-нибудь, кто может предложить решение Swifty для этой проблемы, с которой я столкнулся? знак равно

Заранее спасибо!!


Ну, пока я не нашел действительно удовлетворительного способа сделать это, но я решил опубликовать то, что я сделал для этой конкретной проблемы. Вкратце, решение идет так (с использованием исходного кода примера):

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 для запуска чувствительных строк кода.

Я очень надеюсь, что это поможет кому-то еще, столкнувшись с подобной ситуацией =)


Вы были рядом с решением. Просто нужно сделать это по-другому. Расширять протокол только в том случае, если его часть 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
  }
}




swift-extensions