Инициализация Swift
Инициализация — это процесс подготовки экземпляра класса, структуры или перечисления к использованию. Этот процесс включает установку начального значения для каждого хранимого свойства этого экземпляра и выполнение любых других необходимых задач настройки или инициализации.
Инициализаторы Swift используют метод init().
В отличие от инициализаторов Objective-C, инициализаторы Swift не возвращают значение. Их основная задача — гарантировать, что новые экземпляры правильно инициализированы перед первым использованием.
Экземпляры класса также могут определять деинициализатор для выполнения очистки непосредственно перед освобождением экземпляра класса.
Установка начальных значений для хранимых свойств
Классы и структуры должны устанавливать подходящее начальное значение для всех хранимых свойств при создании экземпляра.
Когда хранимым свойствам присваиваются значения в инициализаторе, их значения устанавливаются напрямую и не вызывают срабатывание наблюдателей свойств.
Процесс присваивания значений хранимым свойствам в инициализаторе:
Создайте начальные значения.
Укажите значения свойств по умолчанию в определении свойства.
Инициализируйте экземпляр и вызовите метод init().
Инициализаторы
Инициализатор вызывается при создании нового экземпляра определённого типа. В простейшей форме он похож на метод экземпляра без параметров, записанный с использованием ключевого слова init.
Синтаксис
init()
{
// 实例化后执行的代码
}
Пример
Следующая структура определяет инициализатор init без параметров и внутри него инициализирует значения хранимых свойств length и breadth значениями 6 и 12:
struct rectangle {
var length: Double
var breadth: Double
init() {
length = 6
breadth = 12
}
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")
Приведенная выше программа выдает следующий вывод:
矩形面积为 72.0
Значения свойств по умолчанию
Вы можете задавать начальные значения для хранимых свойств в инициализаторе; аналогично вы также можете задавать значения по умолчанию при объявлении свойства.
Использование значений по умолчанию делает ваш инициализатор более кратким и понятным, а тип свойства может быть автоматически выведен из его значения по умолчанию.
В следующем примере мы задаём значения по умолчанию при объявлении свойств:
struct rectangle {
// 设置默认值
var length = 6
var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")
Приведенная выше программа выдает следующий вывод:
矩形面积为 72
Параметры инициализатора
Вы можете указывать параметры инициализатора при определении инициализатора init(), как показано ниже:
struct Rectangle {
var length: Double
var breadth: Double
var area: Double
init(fromLength length: Double, fromBreadth breadth: Double) {
self.length = length
self.breadth = breadth
area = length * breadth
}
init(fromLeng leng: Double, fromBread bread: Double) {
self.length = leng
self.breadth = bread
area = leng * bread
}
}
let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("面积为: \(ar.area)")
let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面积为: \(are.area)")
Приведенная выше программа выдает следующий вывод:
面积为: 72.0 面积为: 432.0
Внутренние и внешние имена параметров
Как и у параметров функций и методов, у параметров инициализатора также есть внутреннее имя параметра, используемое внутри инициализатора, и внешнее имя параметра, используемое при вызове инициализатора.
Однако у инициализаторов нет различимого имени перед круглыми скобками, как у функций и методов. Поэтому при вызове инициализатора имена и типы параметров в инициализаторе в основном используются для определения того, какой инициализатор должен быть вызван.
Если вы не указываете внешние имена для параметров при определении инициализатора, Swift автоматически создаёт внешнее имя, совпадающее с внутренним именем, для каждого параметра инициализатора.
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
print("red 值为: \(magenta.red)")
print("green 值为: \(magenta.green)")
print("blue 值为: \(magenta.blue)")
// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let halfGray = Color(white: 0.5)
print("red 值为: \(halfGray.red)")
print("green 值为: \(halfGray.green)")
print("blue 值为: \(halfGray.blue)")
Приведенная выше программа выдает следующий вывод:
red 值为: 1.0 green 值为: 0.0 blue 值为: 1.0 red 值为: 0.5 green 值为: 0.5 blue 值为: 0.5
Параметры инициализатора без внешних имён
Если вы не хотите указывать внешнее имя для параметра инициализатора, можно использовать символ подчёркивания_чтобы явно указать его внешнее имя.
struct Rectangle {
var length: Double
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
//不提供外部名字
init(_ area: Double) {
length = area
}
}
// 调用不提供外部名字
let rectarea = Rectangle(180.0)
print("面积为: \(rectarea.length)")
// 调用不提供外部名字
let rearea = Rectangle(370.0)
print("面积为: \(rearea.length)")
// 调用不提供外部名字
let recarea = Rectangle(110.0)
print("面积为: \(recarea.length)")
Приведенная выше программа выдает следующий вывод:
面积为: 180.0 面积为: 370.0 面积为: 110.0
Опциональные типы свойств
Если ваш пользовательский тип содержит сохраняемое свойство, которое логически может не иметь значения, его нужно определить как опциональный тип (опциональный тип свойства).
Когда сохраняемое свойство объявлено как опциональное, оно автоматически инициализируется значением nil.
struct Rectangle {
var length: Double?
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")
let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")
let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")
Приведенная выше программа выдает следующий вывод:
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
Присваивание значений константным свойствам во время инициализации
Пока значение константы может быть определено к концу инициализации, вы можете изменить значение константного свойства в любой момент во время инициализации.
Для экземпляра класса его константное свойство может быть изменено только во время инициализации класса, который его определяет; его нельзя изменить в подклассе.
Даже если свойство length теперь является константой, мы всё ещё можем задать его значение в инициализаторе его класса:
struct Rectangle {
let length: Double?
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")
let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")
let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")
Приведенная выше программа выдает следующий вывод:
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
Инициализаторы по умолчанию
Инициализатор по умолчанию просто создаёт экземпляр, в котором все значения свойств установлены в значения по умолчанию:
В следующем примере все свойства класса ShoppingListItem имеют значения по умолчанию, и поскольку это базовый класс без суперкласса, он автоматически получает инициализатор по умолчанию, который может устанавливать значения по умолчанию для всех свойств.
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
print("名字为: \(item.name)")
print("数理为: \(item.quantity)")
print("是否付款: \(item.purchased)")
Приведенная выше программа выдает следующий вывод:
名字为: nil 数理为: 1 是否付款: false
Поэлементные инициализаторы для типов структур
Если структура предоставляет значения по умолчанию для всех своих хранимых свойств и не предоставляет никаких пользовательских инициализаторов, она автоматически получает поэлементный инициализатор.
При вызове поэлементного инициализатора вы передаёте значения, используя имена параметров, совпадающие с именами свойств-членов, тем самым выполняя начальное присваивание свойств-членов.
В следующем примере определяется структура Rectangle с двумя свойствами: length и breadth. Swift может автоматически вывести их типы как Double из начальных значений 100.0 и 200.0.
struct Rectangle {
var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)
print("矩形的面积: \(area.length)")
print("矩形的面积: \(area.breadth)")
Поскольку оба хранимых свойства имеют значения по умолчанию, структура Rectangle автоматически получает поэлементный инициализатор init(width:height:). Вы можете использовать его для создания новых экземпляров Rectangle.
Приведенная выше программа выдает следующий вывод:
矩形的面积: 24.0 矩形的面积: 32.0
Делегирование инициализатора для типов значений
Инициализатор может вызывать другие инициализаторы, чтобы завершить часть процесса инициализации экземпляра. Этот процесс называется делегированием инициализаторов и позволяет уменьшить дублирование кода среди нескольких инициализаторов.
В следующем примере структура Rect вызывает инициализаторы Size и Point:
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
// origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size 结构体初始值: \(basicRect.size.width, basicRect.size.height) ")
print("Rect 结构体初始值: \(basicRect.origin.x, basicRect.origin.y) ")
// 将origin和size的参数值赋给对应的存储型属性
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
print("Size 结构体初始值: \(originRect.size.width, originRect.size.height) ")
print("Rect 结构体初始值: \(originRect.origin.x, originRect.origin.y) ")
//先通过center和size的值计算出origin的坐标。
//然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
print("Size 结构体初始值: \(centerRect.size.width, centerRect.size.height) ")
print("Rect 结构体初始值: \(centerRect.origin.x, centerRect.origin.y) ")
Приведенная выше программа выдает следующий вывод:
Size 结构体初始值: (0.0, 0.0) Rect 结构体初始值: (0.0, 0.0) Size 结构体初始值: (5.0, 5.0) Rect 结构体初始值: (2.0, 2.0) Size 结构体初始值: (3.0, 3.0) Rect 结构体初始值: (2.5, 2.5)
Правила делегирования инициализаторов
| Типы значений | Типы классов |
|---|---|
| Типы значений не поддерживают наследование, поэтому процесс делегирования инициализаторов относительно прост, так как они могут делегировать только другим инициализаторам, предоставленным ими самими. Вы можете использовать self.init в пользовательском инициализаторе, чтобы ссылаться на другие инициализаторы того же типа значения. | Он может наследоваться от других классов, что означает, что класс отвечает за то, чтобы все его унаследованные сохранённые свойства также были правильно инициализированы во время создания. |
Наследование классов и инициализация
Swift предоставляет два типа инициализаторов классов, чтобы гарантировать, что все сохранённые свойства в экземпляре класса могут получить начальные значения. Это назначенные инициализаторы и удобные инициализаторы.
| Назначенные инициализаторы | Удобные инициализаторы |
| Основной инициализатор для класса. | Вспомогательный, поддерживающий инициализатор для класса. |
| Он инициализирует все свойства, введённые классом, и вызывает инициализатор суперкласса вверх по цепочке суперклассов для инициализации суперкласса. | Вы можете определить удобный инициализатор, чтобы вызвать назначенный инициализатор в том же классе и предоставить значения по умолчанию для его параметров. Вы также можете определить удобный инициализатор для создания экземпляра для определённой цели или конкретного ввода. |
| Каждый класс должен иметь как минимум один назначенный инициализатор. | Предоставляйте удобные инициализаторы для класса только при необходимости. |
Init(parameters) {
statements
}
|
convenience init(parameters) {
statements
}
|
Пример назначенного инициализатора
class mainClass {
var no1 : Int // 局部存储变量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int // 新的子类存储变量
init(no1 : Int, no2 : Int) {
self.no2 = no2 // 初始化
super.init(no1:no1) // 初始化超类
}
}
let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)
print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")
Приведенная выше программа выдает следующий вывод:
res 为: 10 res 为: 10 res 为: 20
Пример удобного инициализатора
class mainClass {
var no1 : Int // 局部存储变量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int
init(no1 : Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// 便利方法只需要一个参数
override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = mainClass(no1: 20)
let res2 = subClass(no1: 30, no2: 50)
print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")
Приведенная выше программа выдает следующий вывод:
res 为: 20 res2 为: 30 res2 为: 50
Наследование и переопределение инициализаторов
Подкласс в Swift по умолчанию не наследует инициализаторы суперкласса.
Инициализаторы суперкласса наследуются только в определённых и безопасных обстоятельствах.
Когда вы переопределяете назначенный инициализатор суперкласса, вам нужно написать модификатор override.
class SuperClass {
var corners = 4
var description: String {
return "\(corners) 边"
}
}
let rectangle = SuperClass()
print("矩形: \(rectangle.description)")
class SubClass: SuperClass {
override init() { //重载构造器
super.init()
corners = 5
}
}
let subClass = SubClass()
print("五角型: \(subClass.description)")
Приведенная выше программа выдает следующий вывод:
矩形: 4 边 五角型: 5 边
Пример назначенного и удобного инициализатора
Следующий пример продемонстрирует назначенные инициализаторы, удобные инициализаторы и автоматическое наследование инициализаторов в действии.
Он определяет иерархию классов, содержащую два класса, MainClass и SubClass, и продемонстрирует, как взаимодействуют их инициализаторы.
class MainClass {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[匿名]")
}
}
let main = MainClass(name: "Example")
print("MainClass 名字为: \(main.name)")
let main2 = MainClass()
print("没有对应名字: \(main2.name)")
class SubClass: MainClass {
var count: Int
init(name: String, count: Int) {
self.count = count
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, count: 1)
}
}
let sub = SubClass(name: "Example")
print("MainClass 名字为: \(sub.name)")
let sub2 = SubClass(name: "Example", count: 3)
print("count 变量: \(sub2.count)")
Приведенная выше программа выдает следующий вывод:
MainClass 名字为: Example 没有对应名字: [匿名] MainClass 名字为: Example count 变量: 3
Провальные инициализаторы
Если объект класса, структуры или типа перечисления может дать сбой в процессе собственного создания, определите для него провальный инициализатор.
Возможные причины сбоя инициализации переменной включают:
Передача недопустимых значений параметров.
Отсутствие какого-либо необходимого внешнего ресурса.
Не было выполнено определённое условие.
Чтобы правильно обрабатывать ситуации, в которых процесс создания может дать сбой.
Вы можете добавить один или несколько провальных инициализаторов в определение класса, структуры или перечисления. Синтаксис заключается в добавлении вопросительного знака после ключевого слова init (init?).
Пример
В следующем примере определяется структура с именем Animal, имеющая константное свойство с именем species типа String.
Структура также определяет провальный инициализатор, который принимает параметр String с именем species. Этот провальный инициализатор используется для проверки, является ли переданный параметр пустой строкой. Если это пустая строка, провальный инициализатор не может создать объект; в противном случае он успешно завершается.
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
//通过该可失败构造器来构建一个Animal的对象,并检查其构建过程是否成功
// someCreature 的类型是 Animal? 而不是 Animal
let someCreature = Animal(species: "长颈鹿")
// 打印 "动物初始化为长颈鹿"
if let giraffe = someCreature {
print("动物初始化为\(giraffe.species)")
}
Вывод выполнения вышеуказанной программы:
Animal initialized as giraffe
Провальные инициализаторы для типов перечислений
Вы можете получить конкретный член перечисления в типе перечисления, создав проваливающийся инициализатор с одним или несколькими параметрами.
Пример
В следующем примере определяется тип перечисления с именем TemperatureUnit, который содержит три возможных члена перечисления (Kelvin, Celsius и Fahrenheit) и проваливающийся инициализатор, используемый для поиска члена перечисления, соответствующего значению Character:
enum TemperatureUnit {
// 开尔文,摄氏,华氏
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("这是一个已定义的温度单位,所以初始化成功。")
}
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("这不是一个已定义的温度单位,所以初始化失败。")
}
Вывод выполнения вышеуказанной программы:
This is a defined temperature unit, so initialization succeeded. This is not a defined temperature unit, so initialization failed.
Проваливающиеся инициализаторы для типов классов
Проваливающиеся инициализаторы для типов-значений (таких как структуры или перечисления) не имеют ограничений на то, когда и где они вызывают сбой инициализации.
Однако проваливающийся инициализатор класса может вызвать сбой только после того, как все свойства класса инициализированы и выполнены все вызовы делегирования между инициализаторами класса.
Пример
В следующем примере определяется класс с именем StudRecord. Поскольку свойство studname является константой, после успешного создания класса StudRecord свойство studname гарантированно имеет значение, отличное от nil.
class StudRecord {
let studname: String!
init?(studname: String) {
self.studname = studname
if studname.isEmpty { return nil }
}
}
if let stname = StudRecord(studname: "失败构造器") {
print("模块为 \(stname.studname)")
}
Вывод выполнения вышеуказанной программы:
Module is a failed constructor
Переопределение проваливающегося инициализатора
Как и другие инициализаторы, вы также можете переопределить проваливающийся инициализатор базового класса проваливающимся инициализатором подкласса.
Или вы можете переопределить проваливающийся инициализатор базового класса непроваливающимся инициализатором подкласса.
Вы можете использовать непроваливающийся инициализатор для переопределения проваливающегося инициализатора, но обратное не работает.
Непроваливающийся инициализатор никогда не может делегировать проваливающемуся инициализатору.
Пример
В следующем примере описываются проваливающиеся и непроваливающиеся инициализаторы:
class Planet {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[No Planets]")
}
}
let plName = Planet(name: "Mercury")
print("行星的名字是: \(plName.name)")
let noplName = Planet()
print("没有这个名字的行星: \(noplName.name)")
class planets: Planet {
var count: Int
init(name: String, count: Int) {
self.count = count
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, count: 1)
}
}
Вывод выполнения вышеуказанной программы:
行星的名字是: Mercury 没有这个名字的行星: [No Planets]
Инициализатор init! с возможностью отказа
Обычно мы определяем инициализатор с возможностью отказа, добавляя вопросительный знак после ключевого слова init (init?), но также можно определить инициализатор с возможностью отказа, добавив восклицательный знак после init (init!). Пример:
struct StudRecord {
let stname: String
init!(stname: String) {
if stname.isEmpty {return nil }
self.stname = stname
}
}
let stmark = StudRecord(stname: "Example")
if let name = stmark {
print("指定了学生名")
}
let blankname = StudRecord(stname: "")
if blankname == nil {
print("学生名为空")
}
Вывод выполнения вышеуказанной программы:
Student name specified Student name is emptyДругие расширения