Подтвердить что ты не робот

Зачем использовать инициализаторы в классах Swift?

Я пытаюсь понять использование ключевого слова required в классах Swift.

class SomeClass 
{
    required init() {
        // initializer implementation goes here
    }
}

required не заставляет меня реализовать метод в моем дочернем классе. Если я хочу переопределить инициализатор required моего родительского класса, мне нужно написать required, а не override. Я знаю, как это работает, но не могу понять, почему я должен это делать.

В чем преимущество required? Насколько я могу судить, такие языки, как С#, не имеют ничего подобного и отлично работают с override.

4b9b3361

Ответ 1

На самом деле это просто способ удовлетворить компилятор, чтобы убедиться, что если этот класс должен иметь какие-либо подклассы, они наследуют или реализуют этот же инициализатор. В этом вопросе есть сомнения, из-за правила, что если подкласс имеет собственный инициализатор, ни один инициализатор из суперкласса не наследуется. Таким образом, суперкласс может иметь инициализатор, а подкласс не должен иметь его. required преодолевает эту возможность.

Одна ситуация, когда компилятор должен быть удовлетворен таким образом, включает протоколы и работает следующим образом:

protocol Flier {
    init()
}
class Bird: Flier {
    init() {} // compile error
}

Проблема в том, что если бы у Птицы был подкласс, этот подкласс должен был бы реализовать или наследовать init, и вы этого не гарантировали. Маркировка Bird init как required действительно гарантирует.

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

Другая ситуация заключается в том, что у вас есть метод factory, который может сделать класс или его подкласс, вызвав тот же самый инициализатор:

class Dog {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class NoisyDog: Dog {

}

func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
    let d = whattype.init(name: "Fido") // compile error
    return d
}

dogMakerAndNamer вызывает инициализатор init(name:) на подклассе Dog или Dog. Но как может компилятор убедиться, что подкласс будет иметь инициализатор init(name:)? Обозначение required успокаивает страхи компилятора.

Ответ 2

Я хочу обратить внимание на другое решение, предоставленное Required, кроме приведенного выше Мэтта.

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"

}
let instanceSubClass = subClass()
instanceSubClass.name        //output: "Untitled"
instanceSubClass.neakName    //output: "Subclass Untitled"

Как вы можете проверить вышеприведенный пример, я объявил required init() on superClass, init() инициализатор superClass унаследовал по умолчанию на subClass, поэтому вы можете создать экземпляр subClass let instanceSubClass = subClass(),

Но предположим, что вы хотите добавить один назначенный инициализатор на subClass, чтобы присвоить значение времени выполнения хранимому свойству neakName. Конечно, вы можете добавить его, но это приведет к тому, что никакие инициализаторы из суперкласса не будут унаследованы в subClass. Поэтому, если вы создадите экземпляр subClass, вы создадите его через свой назначенный инициализатор, как показано ниже.

class superClass{
    var name: String
    init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"

Здесь вы не сможете создать экземпляр subClass только subClass(), Но если вы хотите, чтобы каждый подкласс суперкласса должен иметь свой собственный init() initializer для создания прямого экземпляра с помощью subClass(). Просто разместите ключевое слово Required до init() на суперклассе, это заставит вас добавить init() initializer на subClass тоже - как показано ниже.

class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}    // Compiler error <------------ required `init()` must be provided by subClass.
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"  

SO, используйте ключевое слово Required перед инициализацией на суперклассе, если вы хотите, чтобы все подклассы были реализованы required initializer суперкласса.

Ответ 3

В соответствии с documentation:

Write the required modifier before the definition of a class initializer to
indicate that every subclass of the class must implement that initializer

Итак, да, требуется, чтобы все дочерние классы реализовали этот конструктор. Однако это не требуется

 if you can satisfy the requirement with an inherited initializer.

Итак, если вы создали более сложные классы, которые не могут быть полностью инициализированы родительским конструктором, вы должны реализовать конструктор require.

Пример из документации (с некоторыми дополнительными материалами):

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    let thisNeedsToBeInitialized: String
    required init() {
        // subclass implementation of the required initializer goes here
        self.thisNeedsToBeInitialized = "default value"
    }
}

Ответ 4

Если вы пытаетесь добавить свой собственный инициализатор в подкласс, тогда вам нужно следовать определенным вещам, которые были объявлены в суперклассе. Поэтому убедитесь, что вы не забудете реализовать этот необходимый метод. Если вы забудете компилятор, вы получите сообщение об ошибке // fatal error, we've not included the required init() . Другая причина заключается в том, что он создает набор условий, которым должен следовать подчиненный класс, подкласс определяет его собственный инициализатор.