Поиск по сайту:

Быстрая инициализация()


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

Быстрая инициализация()

Инициализация — это процесс подготовки экземпляра класса, структуры или перечисления к использованию. Этот процесс включает в себя установку начального значения для каждого сохраненного свойства в этом экземпляре и выполнение любой другой настройки или инициализации, которые требуются до того, как новый экземпляр будет готов к использованию.

Инициализаторы похожи на конструкторы в программировании на Java. Swift, являющийся типобезопасным языком, поместил множество правил для инициализаторов. Это может быть сложно реализовать, если вы хорошо не усвоили концепцию.

Синтаксис быстрой инициализации()

init() {
    // initialise the stored properties here.
}

Давайте посмотрим на пример класса ниже.

class A{
    
    //Compilation error. No initializer is defined.
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
}

Класс выше не будет компилироваться. Компилятор Swift жалуется, что сохраненные свойства не инициализированы. Сохраненные свойства не могут находиться в неопределенном состоянии. Это оставляет нам два возможных варианта:

  1. Назначьте значение свойства по умолчанию в самом определении свойства.
  2. Используйте инициализатор init() для инициализации свойств.

Давайте рассмотрим каждый из подходов по одному.

class A{
    
    var a : Int = 5
    var b : String = "Hello. How you're doing"
    var c : Int?
    let website = "JournalDev"
}

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

var object = A()
object.a = 10
object.c = 2

Второй способ — инициализировать сохраненные свойства с помощью метода init(), как показано ниже.

class A{
    
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
    
    init(a: Int, b: String) {
        self.a = a
        self.b = b
    }
}

var object = A(a: 5, b: "Hello World")

Примечание. SwiftOptional не является хранимым свойством. Следовательно, их не нужно инициализировать. Доступ к сохраненным свойствам осуществляется внутри метода init() с использованием свойства self. Примечание. self используется для ссылки на текущий экземпляр в его собственных методах экземпляра (аналогично this в java). Приведенный выше инициализатор является основным инициализатором класса. Он также известен как назначенный инициализатор (мы обсудим это позже). Инициализаторы также позволяют нам изменять постоянное свойство.

class A{
    
    var a : Int
    var b : String
    var c : Int?
    let website : String
    
    init(a: Int, b: String, website: String) {
        self.a = a
        self.b = b
        self.website = website
    }
}

var object = A(a: 5,b: "Hello World", website: "JournalDev")

Почленные инициализаторы для структур

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

struct Rect{
    var length : Int
    var breadth : Int
}
var r = Rect(length: 5, breadth: 10)
struct Rect{
    var length : Int = 5
    var breadth : Int = 10
}

var r = Rect()
var r1 = Rect(length: 10, breadth: 5)

Поскольку мы присвоили значения по умолчанию сохраненным свойствам в приведенном выше фрагменте, мы получаем инициализатор по умолчанию без инициализации члена вместе с инициализатором для каждого члена.

struct Rect{
    var length : Int
    var breadth : Int
    
    init(length: Int, breadth: Int) {
        self.length =  length + 10
        self.breadth = breadth + 10
    }
}
var r = Rect(length: 10, breadth: 5)

В приведенном выше случае мы определили собственный пользовательский инициализатор. Использование параметров без внешнего имени Когда внешнее имя для инициализатора не требуется, для обозначения того же, что показано ниже, используется подчеркивание «_».

class A{
    
    var a : Int
    var b : String
    var c : Int?
    let website = "JournalDev"
    
    init(_ a: Int, _ b: String) {
        self.a = a
        self.b = b
    }
}

var object = A(5,"Hello World")
struct Rect{
    var length : Int
    var breadth : Int
    
    init(_ length: Int, _ breadth: Int) {
        self.length =  length + 10
        self.breadth = breadth + 10
    }
}
var r = Rect(10, 10)

Типы инициализаторов Swift

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

  1. Назначенные инициализаторы: это основной инициализатор класса. Он должен полностью инициализировать все свойства, введенные его классом, перед вызовом любого инициализатора суперкласса. Класс может иметь более одного назначенного инициализатора. В каждом классе должен быть хотя бы один назначенный инициализатор.
  2. Удобные инициализаторы. Это вспомогательные инициализаторы для класса. Они должны вызывать назначенный инициализатор того же класса. Они являются необязательными и могут использоваться для пользовательской настройки. Они написаны в том же стиле, но с добавлением модификатора удобство перед ключевым словом init

class Student{
    
    var name : String
    var degree : String
    
    init(name : String, degree: String) {
        self.name = name
        self.degree = degree
    }
    
    convenience init()
    {
        self.init(name: "Unnamed", degree: "Computer Science")
    }
    
}
var student = Student()
student.degree // "Computer Science"
student.name // "Unnamed"

Удобные инициализаторы полезны, когда дело доходит до назначения значений по умолчанию сохраненным свойствам.

Делегирование Swift Initializer для типов значений

Можно вызвать инициализатор из другого, тем самым избегая дублирования кода. Типы значений, такие как структуры, не поддерживают наследование. Следовательно, единственный возможный способ — вызвать инициализатор внутри той же структуры. Пример приведен ниже.

struct Rect{
    var length : Int
    var breadth : Int
    
    init(_ length: Int, _ breadth: Int) {
        self.length =  length
        self.breadth = breadth
    }
    
    init(_ length: Int)
    {
        self.init(length, length)
    }
}
var r = Rect(10, 5)
var r1 = Rect(15) //initialises the length and breadth to 15

Делегирование Swift Initializer для ссылочных типов

Классы, являющиеся ссылочными типами, поддерживают наследование. Таким образом, инициализаторы также могут вызывать другие инициализаторы из суперкласса, тем самым добавляя обязанности по правильному наследованию и инициализации всех значений. Ниже приведены основные правила, определенные для обработки отношений между инициализаторами.

  • Назначенный инициализатор должен вызывать назначенный инициализатор из своего непосредственного суперкласса.
  • Удобный инициализатор должен вызывать другой инициализатор из того же класса.
  • Удобный инициализатор должен в конечном итоге вызвать назначенный инициализатор.

На следующем рисунке описаны приведенные выше правила.

Назначенные инициализаторы всегда должны делегировать вверх. Инициализаторы удобства всегда должны делегировать друг другу. ключевое слово super невозможно для удобного инициализатора в подклассе.

Наследование и переопределение Swift Initializer

Подклассы в Swift не наследуют инициализаторы своего суперкласса по умолчанию, если не выполняются определенные условия (автоматическое наследование инициализаторов). Это сделано для предотвращения полусырой инициализации в подклассе. Давайте посмотрим, как назначенные и удобные инициализаторы работают через наследование. Мы определим базовый класс Vehicle, который будет унаследован соответствующими подклассами. Мы будем использовать Enums в качестве типа в классах. Наш базовый класс Vehicle определен, как показано ниже.

enum VehicleType : String {
    case twoWheeler = "TwoWheeler"
    case fourWheeler = "FourWheeler"
}

class Vehicle{
    
    var vehicleType : VehicleType
    
    init(vehicleType: VehicleType) {
        self.vehicleType = vehicleType
        print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
    }
    
    convenience init()
    {
        self.init(vehicleType: .fourWheeler)
    }
}

var v = Vehicle(vehicleType: .twoWheeler)

Примечание. Удобный инициализатор должен вызывать назначенный инициализатор того же класса с помощью self.init. Давайте определим подкласс вышеуказанного класса, как показано ниже.

enum TwoWheelerType : String
{
    case scooty = "Scooty"
    case bike = "Bike"
}

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
}

Важные моменты, на которые следует обратить внимание:

  • Назначенный инициализатор подкласса должен инициализировать свои собственные свойства перед вызовом назначенного инициализатора суперкласса.
  • Подкласс может изменять унаследованные свойства суперкласса только после вызова super.init.

Следующий код приведет к ошибке времени компиляции.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        self.vehicleType = vType //Won't compile
        super.init(vehicleType: vType)
        //self.vehicleType = .fourWheeler //This would work.
        
    }
}

var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)

Как объяснялось ранее, инициализатор суперкласса не наследуется автоматически в подклассе. Таким образом, приведенная ниже инициализация завершится ошибкой.

var t = TwoWheeler(vehicleType: .twoWheeler) //manufacturer property isn't initialized.

Чтобы переопределить инициализатор, инициализатор подкласса должен соответствовать назначенному инициализатору суперкласса. В этом случае к инициализатору добавляется ключевое слово override.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
    override init(vehicleType: VehicleType)
    {
        print("Class TwoWheeler. Overriden Initializer. \(vehicleType.rawValue)")
        self.twoWheelerType = .bike
        self.manufacturer = "Not defined"
        super.init(vehicleType: vehicleType)
    }



Приведенный ниже инициализатор не переопределяет инициализатор из суперкласса, поскольку имя параметра отличается.

//This would give a compile-time error since the parameter v doesn't match with the superclass.
override init(v: VehicleType)
    {
        self.twoWheelerType = .bike
        self.manufacturer = "Not defined"
        super.init(vehicleType: v)
    }

Использование Convenience Initializer для переопределения одного из суперкласса.

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
    override convenience init(vehicleType: VehicleType) {
        self.init(twoWheelerType: .bike, manufacturer: "Not Defined", vType: .twoWheeler)
        self.vehicleType = vehicleType
    }
}
var t = TwoWheeler(twoWheelerType: .scooty, manufacturer: "Hero Honda", vType: .twoWheeler)
t = TwoWheeler(vehicleType: .twoWheeler)

//Output
Following gets printed on the console:
Class TwoWheeler. Scooty manufacturer is Hero Honda
Class Vehicle. vehicleType is TwoWheeler

Class TwoWheeler. Bike manufacturer is Not Defined
Class Vehicle. vehicleType is TwoWheeler

К инициализатору удобства добавлено ключевое слово override. Он вызывает назначенный инициализатор того же класса. Примечание. Порядок ключевых слов удобство и override не имеет значения.

Требуемые инициализаторы

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

class Vehicle{
    
    var vehicleType : VehicleType
    
    required init(vehicleType: VehicleType) {
        self.vehicleType = vehicleType
        print("Class Vehicle. vehicleType is \(self.vehicleType.rawValue)\n")
    }
    
    convenience init()
    {
        self.init(vehicleType: .fourWheeler)
    }
}

class TwoWheeler : Vehicle{
    
    var twoWheelerType : TwoWheelerType
    var manufacturer : String
    
    init(twoWheelerType : TwoWheelerType, manufacturer : String, vType : VehicleType) {
        self.twoWheelerType = twoWheelerType
        self.manufacturer = manufacturer
        print("Class TwoWheeler. \(self.twoWheelerType.rawValue) manufacturer is \(self.manufacturer)")
        super.init(vehicleType: vType)
        
    }
    
     required init(vehicleType: VehicleType) {
        self.manufacturer = "Not Defined"
        self.twoWheelerType = .bike
        super.init(vehicleType: vehicleType)
    }
}

Примечание. Добавление обязательного модификатора означает, что инициализатор будет переопределен. Следовательно, ключевое слово override в приведенном выше случае может быть опущено. Использование обязательного инициализатора с удобством Обязательный и удобный инициализаторы не зависят друг от друга и могут использоваться вместе. Давайте создадим еще один подкласс Vehicle, чтобы продемонстрировать совместное использование модификаторов required и convenience.

enum FourWheelerType : String
{
    case car = "Car"
    case bus = "Bus"
    case truck = "Truck"
}


class FourWheeler : Vehicle
{
    var fourWheelerType : FourWheelerType
    var name : String
    
    init(fourWheelerType : FourWheelerType, name: String, vehicleType: VehicleType) {
        self.fourWheelerType = fourWheelerType
        self.name = name
        print("Class FourWheeler. \(self.fourWheelerType.rawValue) Model is \(self.name)")
        super.init(vehicleType: vehicleType)
        self.vehicleType = vehicleType
    }
    
    required convenience init(vehicleType: VehicleType) {
        self.init(fourWheelerType: .bus, name: "Mercedes", vehicleType: vehicleType)
    }
}


class Car : FourWheeler{
    
    var model : String
    
    init(model: String) {
        self.model = model
        print("Class Car. Model is \(self.model)")
        super.init(fourWheelerType: .car, name: self.model, vehicleType: .fourWheeler)
    }
    
    required init(vehicleType: VehicleType)
    {
        self.model = "Not defined"
        print("Class Car. Model is \(self.model)")
        super.init(fourWheelerType: .car, name: self.model, vehicleType: vehicleType)
    }
    
}

Важные вещи, которые следует отметить в приведенном выше фрагменте кода:

  • Удобные инициализаторы — это вторичные инициализаторы в классе.
  • Настройка удобного инициализатора как обязательного означает, что его реализация в подклассе обязательна.

Автоматическое наследование инициализатора

Есть два обстоятельства, при которых подкласс автоматически наследует инициализаторы от суперкласса.

  • Не определяйте назначенные инициализаторы в своем подклассе.
  • Реализовать все назначенные инициализаторы суперкласса. Все удобные инициализаторы также будут автоматически унаследованы.

Первое правило в действии показано во фрагменте ниже:

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

class Tutorial: Name {
    
    var tutorial : String? = "Swift Initialization"
}

var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")

Второе правило в действии показано во фрагменте ниже.

class Name {
    
    var name: String
    
    init(n: String) {
        self.name = n
    }
    
    convenience init()
    {
        self.init(n: "No name assigned")
    }
}

class Tutorial: Name {
    
    var tutorial : String? = "Swift Tutorial"
    
    override init(n : String) {
        super.init(n: n)
    }
}

var parentObject = Name(n: "Anupam")
var childObject = Tutorial(n: "JournalDev")
var childObject2 = Tutorial()
print(childObject2.name) //prints "No name assigned

Удобный инициализатор суперкласса автоматически доступен в подклассе в приведенном выше коде.

Быстрый отказоустойчивый инициализатор

Мы можем определить неудачный инициализатор, используя ключевое слово init? для классов, структур или перечислений, которое срабатывает, когда процесс инициализации завершается сбоем. Инициализация может завершиться неудачей по разным причинам: неверные значения параметров, отсутствие внешнего источника и т. д. Неудачный инициализатор создает необязательное значение того типа, который он инициализирует. Мы будем возвращать nil, чтобы вызвать сбой инициализации (хотя init ничего не возвращает). Неудачные инициализаторы со структурами

struct SName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

var name = SName(name: "JournalDev")
if name != nil {
    print("init success") //this gets displayed
}
else{
    print("init failed")
}
name  = SName(name: "")

if name != nil {
    print("init success")
}
else{
    print("init failed") //this gets displayed
}

Неудачные инициализаторы с перечислениями

enum CharacterExists {
    case A, B
    init?(symbol: Character) {
        switch symbol {
        case "A":
            self = .A
        case "B":
            self = .B
        default:
            return nil
        }
    }
}


let ch = CharacterExists(symbol: "C")
if ch != nil {
    print("Init failed. Character doesn't exist")
}
class CName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
var name  = CName(name: "")

if name != nil {
    print("init success")
}
else{
    print("init failed")
}

Примечание. Неудачный инициализатор и неотказной инициализатор не могут иметь одинаковые типы и имена параметров.

Переопределение сбойного инициализатора

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

class CName {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
var name  = CName(name: "")

class SubName : CName{
    
    var age : Int
    override init(name: String)
    {
        self.age = 23
        super.init(name: name)!  
    }
}

Примечание. Принудительная распаковка используется для вызова сбойного инициализатора из суперкласса как часть реализации стабильного инициализатора подкласса. На этом учебник по быстрой инициализации заканчивается. Ссылки: Документы Apple