Swift Initialization
Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization tasks required.
Swift initializers use the init() method.
Unlike Objective-C initializers, Swift initializers do not return a value. Their main task is to ensure that new instances are correctly initialized before they are used for the first time.
Class instances can also define a deinitializer to perform cleanup work just before the class instance is deallocated.
Setting Initial Values for Stored Properties
Classes and structures must set an appropriate initial value for all stored properties when an instance is created.
When stored properties are assigned values in an initializer, their values are set directly and do not trigger any property observers.
The process of assigning stored properties in an initializer:
Create initial values.
Specify default property values in the property definition.
Initialize the instance and call the init() method.
Initializers
An initializer is called when creating a new instance of a particular type. In its simplest form, it is similar to an instance method with no parameters, written using the keyword init.
Syntax
init()
{
// 实例化后执行的代码
}
Example
The following structure defines an initializer init with no parameters, and inside it initializes the values of the stored properties length and breadth to 6 and 12:
struct rectangle {
var length: Double
var breadth: Double
init() {
length = 6
breadth = 12
}
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")
The above program produces the following output:
矩形面积为 72.0
Default Property Values
You can set initial values for stored properties in an initializer; similarly, you can also set default values when declaring the property.
Using default values makes your initializer more concise and clearer, and the property's type can be automatically inferred from its default value.
In the following example, we set default values when declaring the properties:
struct rectangle {
// 设置默认值
var length = 6
var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")
The above program produces the following output:
矩形面积为 72
Initializer Parameters
You can provide initializer parameters when defining an initializer init(), as shown below:
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)")
The above program produces the following output:
面积为: 72.0 面积为: 432.0
Internal and External Parameter Names
As with function and method parameters, initializer parameters also have an internal parameter name used inside the initializer and an external parameter name used when calling the initializer.
However, initializers do not have a distinguishable name before the parentheses like functions and methods do. Therefore, when calling an initializer, the parameter names and types in the initializer are mainly used to determine which initializer should be called.
If you do not provide external names for parameters when defining an initializer, Swift automatically generates an external name identical to the internal name for each initializer parameter.
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)")
The above program produces the following output:
red 值为: 1.0 green 值为: 0.0 blue 值为: 1.0 red 值为: 0.5 green 值为: 0.5 blue 值为: 0.5
Initializer Parameters Without External Names
If you do not want to provide an external name for a parameter of an initializer, you can use an underscore_to explicitly describe its external name.
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)")
The above program produces the following output:
面积为: 180.0 面积为: 370.0 面积为: 110.0
Optional Property Types
If your custom type contains a stored property that is logically allowed to have no value, you need to define it as an optional type (optional property type).
When a stored property is declared as optional, it is automatically initialized to 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)")
The above program produces the following output:
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
Assigning Constant Properties During Initialization
As long as the value of a constant can be determined by the end of initialization, you can modify the value of the constant property at any point during initialization.
For a class instance, its constant property can only be modified during initialization of the class that defines it; it cannot be modified in a subclass.
Even though the length property is now a constant, we can still set its value in its class's initializer:
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)")
The above program produces the following output:
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
Default Initializers
A default initializer simply creates an instance with all property values set to their defaults:
In the following example, all properties in the ShoppingListItem class have default values, and since it is a base class without a superclass, it automatically gains a default initializer that can set default values for all properties.
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
print("名字为: \(item.name)")
print("数理为: \(item.quantity)")
print("是否付款: \(item.purchased)")
The above program produces the following output:
名字为: nil 数理为: 1 是否付款: false
Memberwise Initializers for Structure Types
If a structure provides default values for all of its stored properties and does not provide any custom initializers, it automatically receives a memberwise initializer.
When calling a memberwise initializer, you pass values using parameter names that match the member property names, thereby completing the initial assignment of the member properties.
The following example defines a structure Rectangle with two properties, length and breadth. Swift can automatically infer their types as Double from the initial values 100.0 and 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)")
Because both stored properties have default values, the Rectangle structure automatically receives a memberwise initializer init(width:height:). You can use it to create new instances of Rectangle.
The above program produces the following output:
矩形的面积: 24.0 矩形的面积: 32.0
Initializer Delegation for Value Types
An initializer can call other initializers to complete part of the instance initialization process. This process is called initializer delegation, and it reduces code duplication among multiple initializers.
In the following example, the Rect structure calls the initializers of Size and 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) ")
The above program produces the following output:
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)
Rules for Initializer Delegation
| Value types | Class types |
|---|---|
| Value types do not support inheritance, so the process of initializer delegation is relatively simple, because they can only delegate to other initializers provided by themselves. You can use self.init in a custom initializer to reference other initializers of the same value type. | It can inherit from other classes, which means a class is responsible for ensuring that all of its inherited stored properties are also correctly initialized during construction. |
Class Inheritance and Initialization
Swift provides two types of class initializers to ensure that all stored properties in a class instance can obtain initial values. They are designated initializers and convenience initializers.
| Designated initializers | Convenience initializers |
| The primary initializer for a class. | A secondary, supporting initializer for a class. |
| It initializes all properties introduced by the class, and calls a superclass initializer up the superclass chain to initialize the superclass. | You can define a convenience initializer to call a designated initializer in the same class and provide default values for its parameters. You can also define a convenience initializer to create an instance for a specific purpose or specific input. |
| Every class must have at least one designated initializer. | Provide convenience initializers for a class only when necessary. |
Init(parameters) {
statements
}
|
convenience init(parameters) {
statements
}
|
Designated Initializer Example
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)")
The above program produces the following output:
res 为: 10 res 为: 10 res 为: 20
Convenience Initializer Example
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)")
The above program produces the following output:
res 为: 20 res2 为: 30 res2 为: 50
Initializer Inheritance and Overriding
A subclass in Swift does not inherit superclass initializers by default.
Superclass initializers are inherited only in certain and safe circumstances.
When you override a superclass designated initializer, you need to write the override modifier.
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)")
The above program produces the following output:
矩形: 4 边 五角型: 5 边
Designated and Convenience Initializer Example
The following example will demonstrate designated initializers, convenience initializers, and automatic initializer inheritance in action.
It defines a class hierarchy containing two classes, MainClass and SubClass, and will demonstrate how their initializers interact.
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)")
The above program produces the following output:
MainClass 名字为: Example 没有对应名字: [匿名] MainClass 名字为: Example count 变量: 3
Failable Initializers
If an object of a class, structure, or enumeration type may fail during the process of constructing itself, define a failable initializer for it.
Possible causes of variable initialization failure include:
Passing invalid parameter values.
Missing some kind of required external resource.
A specific condition was not met.
To properly handle situations where the construction process may fail.
You can add one or more failable initializers to the definition of a class, structure, or enumeration. The syntax is to add a question mark after the init keyword (init?).
Example
The following example defines a structure named Animal, which has a constant property named species of type String.
The structure also defines a failable initializer that takes a String parameter named species. This failable initializer is used to check whether the passed parameter is an empty string. If it is an empty string, the failable initializer fails to construct the object; otherwise, it succeeds.
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)")
}
The output of the above program execution is:
Animal initialized as giraffe
Failable Initializers for Enumeration Types
You can obtain a specific enum member in an enumeration type by constructing a failable initializer with one or more parameters.
Example
The following example defines an enumeration type named TemperatureUnit, which contains three possible enum members (Kelvin, Celsius, and Fahrenheit) and a failable initializer used to find the enum member corresponding to a Character value:
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("这不是一个已定义的温度单位,所以初始化失败。")
}
The output of the above program execution is:
This is a defined temperature unit, so initialization succeeded. This is not a defined temperature unit, so initialization failed.
Failable Initializers for Class Types
Failable initializers for value types (such as structures or enumerations) have no restrictions on when and where they trigger initialization failure.
However, a failable initializer for a class can only trigger failure after all class properties have been initialized and all delegation calls between initializers in the class have occurred.
Example
In the following example, a class named StudRecord is defined. Because the studname property is a constant, once the StudRecord class is successfully constructed, the studname property definitely has a non-nil value.
class StudRecord {
let studname: String!
init?(studname: String) {
self.studname = studname
if studname.isEmpty { return nil }
}
}
if let stname = StudRecord(studname: "失败构造器") {
print("模块为 \(stname.studname)")
}
The output of the above program execution is:
Module is a failed constructor
Overriding a Failable Initializer
Just like other initializers, you can also override a base class's failable initializer with a subclass's failable initializer.
Or you can override a base class's failable initializer with a subclass's non-failable initializer.
You can use a non-failable initializer to override a failable initializer, but the reverse does not work.
A non-failable initializer can never delegate to a failable initializer.
Example
The following example describes failable and non-failable initializers:
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)
}
}
The output of the above program execution is:
行星的名字是: Mercury 没有这个名字的行星: [No Planets]
The init! Failable Initializer
Generally, we define a failable initializer by adding a question mark after the init keyword (init?), but you can also define a failable initializer by adding an exclamation mark after init (init!). Example:
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("学生名为空")
}
The output of the above program execution is:
Student name specified Student name is emptyOther Extensions