Быстрая инициализация()
В этом руководстве по 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 жалуется, что сохраненные свойства не инициализированы. Сохраненные свойства не могут находиться в неопределенном состоянии. Это оставляет нам два возможных варианта:
- Назначьте значение свойства по умолчанию в самом определении свойства.
- Используйте инициализатор
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
Инициализаторы для классов можно разделить на следующие типы:
- Назначенные инициализаторы: это основной инициализатор класса. Он должен полностью инициализировать все свойства, введенные его классом, перед вызовом любого инициализатора суперкласса. Класс может иметь более одного назначенного инициализатора. В каждом классе должен быть хотя бы один назначенный инициализатор.
- Удобные инициализаторы. Это вспомогательные инициализаторы для класса. Они должны вызывать назначенный инициализатор того же класса. Они являются необязательными и могут использоваться для пользовательской настройки. Они написаны в том же стиле, но с добавлением модификатора
удобство
перед ключевым словом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