Статическая функция, возвращающая динамический тип класса [duplicate]

Должно работать следующее:

\?name=(.*?)&
32
задан Rizier123 7 November 2016 в 17:27
поделиться

3 ответа

Как и в Используя «self» в функциях расширения класса в Swift , вы можете определить общий вспомогательный метод, который определяет тип «я» из вызывающего контекста:

extension UIViewController
{
    class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
    {
        return instantiateFromStoryboardHelper(storyboardName, storyboardId: storyboardId)
    }

    private class func instantiateFromStoryboardHelper<T>(storyboardName: String, storyboardId: String) -> T
    {
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        let controller = storyboard.instantiateViewControllerWithIdentifier(storyboardId) as! T
        return controller
    }
}

Затем компилируется

let vc = MyViewController.instantiateFromStoryboard("name", storyboardId: "id")

, и тип выводится как MyViewController.


Обновление для Swift 3:

extension UIViewController
{
    class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
    {
        return instantiateFromStoryboardHelper(storyboardName: storyboardName, storyboardId: storyboardId)
    }

    private class func instantiateFromStoryboardHelper<T>(storyboardName: String, storyboardId: String) -> T
    {
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) as! T
        return controller
    }
}

Другое возможное решение, использующее unsafeDowncast:

extension UIViewController
{
    class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
    {
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: storyboardId)
        return unsafeDowncast(controller, to: self)
    }
}
63
ответ дан Martin R 15 August 2018 в 14:36
поделиться
  • 1
    Привет, Мартин. Не могли бы вы объяснить, как компилятор сможет определить общий тип T и сможет ли он передать его Self? – Adithya 10 September 2016 в 06:13
  • 2
    люди, которые говорят «функция частного класса», а не "статические функции" имеют много стиля. :) – Fattie 5 February 2017 в 16:21
  • 3
    Интересно, что если вы просто вернете базовый тип - не потрудитесь с общим - он отлично работает в runtime (возвращая фактический подкласс), но вам придется приводить результаты в код, при «времени редактора». пример: stackoverflow.com/a/42053648/294884 – Fattie 5 February 2017 в 16:34
  • 4
    @JoeBlow: Да, цель здесь состояла в том, чтобы избежать броска. - "статические" «класс + окончательный» и, следовательно, не имеет отношения к «частному». – Martin R 5 February 2017 в 17:29
  • 5
    вполне. спасибо @MartinR. Мне потребовались дни, чтобы понять ответ на эту концептуально подобную проблему stackoverflow.com/q/42041150/294884 – Fattie 5 February 2017 в 20:48

Self определяется во время компиляции, а не во время выполнения. В вашем коде Self в точности эквивалентен UIViewController, а не «подкласс, который вызывает это». Это вернет UIViewController, и вызывающий должен будет as перевести его в правый подкласс. Я предполагаю, что это то, чего вы пытались избежать (хотя это обычный способ какао), поэтому просто возвращение UIViewController, вероятно, является лучшим решением).

Примечание: вы должны не следует называть функцию initialize в любом случае. Это существующая функция класса NSObject и в лучшем случае приведет к путанице. Ошибки в худшем случае.

Но если вы хотите избежать as вызывающего абонента, подклассом обычно не является инструмент для добавьте функциональность в Swift. Вместо этого вам обычно нужны дженерики и протоколы. В этом случае вам нужны только дженерики.

func instantiateViewController<VC: UIViewController>(storyboardName: String, storyboardId: String) -> VC {
    let storyboad = UIStoryboard(name name: storyboardName, bundle: nil)
    let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! VC

    return controller
}

Это не метод класса. Это просто функция. Здесь нет необходимости в классе.

let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId)
14
ответ дан Rob Napier 15 August 2018 в 14:36
поделиться
  • 1
    Это отличный ответ. У меня была очень похожая проблема некоторое время назад. Мне нужен протокол, который могут быть реализованы в классах, которые могут быть созданы из раскадровки, так что вызывающему не нужно было знать, какой именно тип контроллера создается. Я отказался от этого подхода. Возможно, я попробую еще раз, но с общим в протоколе. – NRitH 18 October 2015 в 16:30

Другой способ - использовать протокол, который также позволяет вам возвращать Self.

protocol StoryboardGeneratable {

}

extension UIViewController: StoryboardGeneratable {

}

extension StoryboardGeneratable where Self: UIViewController
{
    static func initialize(storyboardName: String, storyboardId: String) -> Self
    {
        let storyboad = UIStoryboard(name: storyboardName, bundle: nil)
        let controller = storyboad.instantiateViewController(withIdentifier: storyboardId) as! Self
        return controller
    }
}
0
ответ дан ukim 15 August 2018 в 14:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: