Возврат подкласса в статике UIViewController

Рассмотрим базовый класс UIViewController...

class Rooms: UIViewController {
    class func instantiate()->Rooms {
    }

    static func make()->Rooms {
        let emplacedAndSetup = self.instantiate()
        // various kodes here
        // very likely put s.view somewhere
        return emplacedAndSetup
    }

    sundryOtherFunctionality()
}

(Обратите внимание на self. перед instantiate(), что кажется необходимым для получения "этого" экземпляра.)

Каждый подкласс знает свой собственный идентификатор раскадровки, как использовать его для instantiateViewController:

class Dining: Rooms {
    override class func instantiate()->Dining { // returns a "Dining"
        let d = stbd.instantiateViewController(
            withIdentifier: "Some Specific Scene") as! Dining
        return d
    }
}
class Bath: Rooms {
    override class func instantiate()->Bath { // returns a "Bath"
        let b = stbd.instantiateViewController(
            withIdentifier: "Some Other Scene") as! Bath
        return b
    }
}

Ты можешь это сделать,

let d  = Dining.make()
let r  = Bath.make()

Единственная небольшая проблема в том, что он возвращает базовый класс. НО СМОТРИТЕ НИЖЕ. Так что на практике вы должны

let d  = Dining.make() as! Dining
let r  = Bath.make() as! Bath

Есть ли способ изменить статический make так, чтобы Dining.make() действительно возвращал Dining, а Bath.make() возвращал Bath?

( @Hamish указал, что можно использовать шаблон init и Self, метод, который возвращает объект типа, который был вызван из Однако я думаю, что это невозможно из-за instantiateViewController. )


Так. Скажем, у вас есть код вроде

let d = Dining.make(blah blah)

по факту. Во время выполнения d становится "столовой", а не "комнатой"

это чудесно.

Но. Если вы сделаете это в IDE

let d:Dining = Dining.make(blah blah)

он терпит неудачу - он думает, что d будет Room, не Dining.

Итак, весь ваш код должен выглядеть так:

let d = Dining.make(blah blah) as! Dining

что отстой. Как исправить?


Обратите внимание, просто TBC решение состоит в том, чтобы сделать статику универсальной, как в ответе MartinR здесь https://stackoverflow.com/a/33200426/294884 Пример кода в ответе ниже.


person Fattie    schedule 04.02.2017    source источник
comment
Вы можете использовать инициализатор required вместо статического метода instantiate() (а затем переопределить этот инициализатор в своих подклассах), а затем вы можете использовать возвращаемый тип Self для make(), см., например, эти вопросы и ответы.   -  person Hamish    schedule 04.02.2017
comment
Хм, я понимаю, @Hamish, я слишком упростил код примера - это контроллер представления, который создает себя на лету. Редактирование..   -  person Fattie    schedule 04.02.2017
comment
Похоже, вы в основном просто хотите это без параметров для метода instantiateFromStoryboard, поскольку вы сами их определяете.   -  person Hamish    schedule 05.02.2017
comment
вы правы в том, что ответ Мартина использует такой общий, как всегда, спасибо @Hamish   -  person Fattie    schedule 05.02.2017
comment
интересно, что если вы просто возвращаете базовый тип - не беспокойтесь об универсальном - он отлично работает во время выполнения (возвращая фактический подкласс), но вам придется преобразовывать результаты в код во время редактирования. пример: stackoverflow.com/a/42053648/294884   -  person Fattie    schedule 05.02.2017
comment
Ну да, если вы правильно настроили раскадровку. Проблема в том, что instantiateViewController не знает динамический тип экземпляра, который он возвращает, до времени выполнения — таким образом, он возвращает экземпляр, статически типизированный как UIViewController. Приведение не изменяет динамический тип экземпляра — оно просто изменяет статический тип, добавляя утверждение во время выполнения, что указанный статический тип является правильным.   -  person Hamish    schedule 05.02.2017
comment
(к смущению, я вставил этот комментарий не на ту страницу!) Проблема в том, что instanceiateViewController не знает, какой тип он возвращает до момента выполнения Точно. В точку. Мой подход здесь решает эту проблему. Решение состоит в том, что каждый подкласс должен иметь раздражающую маленькую функцию, поэтому выше override class func instantiate()->Dining { // returns a "Dining" и то же самое для всех остальных. Как только вы предпримете раздражающие усилия, чтобы вырезать и вставить их для каждого подкласса, все готово. Затем вы получаете реальный класс во время редактирования без необходимости каждый раз приводить. ууу   -  person Fattie    schedule 05.02.2017
comment
хорошо, мой молодой мастер-механик, вы сделали это снова :) при этом я не уверен, что подход AppzYourLife не лучше.   -  person Fattie    schedule 05.02.2017


Ответы (2)


Вы можете сделать что-то вроде этого.

class RoomBase: RoomProtocol {
    // things common to every room go here
    required init() {}
}

Вы можете поместить RoomBase все, что хотите, чтобы другие комнаты унаследовали.

Затем вы помещаете метод make() в расширение протокола.

protocol RoomProtocol: class {
    init()
}

extension RoomProtocol where Self: RoomBase {
    static func make() -> Self {
        let room = Self()
        // set up
        return room
    }
}

Теперь вы можете написать

class Dining: RoomBase {}
class Bath: RoomBase { }

И этот код будет работать

let dining: Dining = Dining.make()
let bath: Bath = Bath.make()
person Luca Angeletti    schedule 04.02.2017
comment
@JoeBlow: я понимаю вашу точку зрения о классе Rooms, который в вашем сценарии не может быть представлен в виде протокола. Позвольте мне немного подумать об этом. Наконец, что касается абстрактных методов, я все же предлагаю избегать их, но я уважаю вашу точку зрения. - person Luca Angeletti; 04.02.2017
comment
@JoeBlow Хороший вопрос. Просто переместил эту часть в конец моего ответа. - person Luca Angeletti; 04.02.2017
comment
@JoeBlow Я обновил свой ответ, дайте мне знать, что вы думаете - person Luca Angeletti; 04.02.2017

Я не люблю давать свой собственный ответ, но решение таково..

Итак, проблема в том, что во время редактирования

let d = Dining.make()

"не работает", вы должны сделать это

let d = Dining.make() as! Dining

(Он РАБОТАЕТ во время компиляции, d становится Dining: он не «работает» во время редактирования.)

Итак, решение

static func make()->Rooms {
    let emplacedAndSetup = self.instantiate()
    return emplacedAndSetup
}

становится

static func make<T: Rooms>()->T {
    let emplacedAndSetup = self.instantiate() as! T
    return emplacedAndSetup
}

Итак, это все.

Примечание. Вполне возможно, что решение AppzForLife работает и/или лучше в качестве универсального "автоустановщика UIViewController", но это ответ на вопрос как таковой.

person Fattie    schedule 05.02.2017