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
:
-
UIViewController
APIMyProtocol
иUIViewController
API - Переопределите метод
UIViewController
initialize()
, чтобы иметь возможность swizzleviewWillAppear:
Эти 2 возможности кажутся несовместимыми (с Swift 3), потому что:
- Мы не можем расширять классы с условиями (например,
extension UIViewController where Self: MyProtocol
) - если мы вместо этого расширим протокол , мы можем добавить
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
}
}