Swift constructs the process
The construction process is a preparation process that uses instances of a class, structure, or enumeration type. T his process involves setting the initial value for each property in the instance and performing the necessary preparation and initialization tasks for it.
Swift constructors use the init() method.
Unlike constructors in Objective-C, Swift constructors do not need to return values, and their primary task is to ensure that new instances are properly initialized before first use.
Class instances can also clean up memory by defining a destructor (deinitializer) before the class instance is released.
The initial assignment of the storage property
When classes and structures are created, the appropriate initial values must be set for all storage properties.
When storage properties are assigned in the constructor, their values are set directly and do not trigger any property observers.
Store properties in the constructor to assign processes:
-
Create the initial value.
-
Specify the default property value in the property definition.
-
Initialize the instance and call the init() method.
Constructor
The constructor is called when a new instance of a particular type is created. I ts simplest form is similar to an instance method without any parameters, named after the keyword init.
Grammar
init() { // 实例化后执行的代码 }
Instance
The following structure defines a constructor init without parameters, in which the values of the storage properties length and breadth are initialized 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 output of the above program execution is:
矩形面积为 72.0
The default property value
We can set an initial value for a storage property in the constructor, or we can set a default value for a property when it is declared.
Using default values makes your constructor cleaner, clearer, and automatically derives the type of property by default.
The following instances set default values for properties when they are declared:
struct rectangle { // 设置默认值 var length = 6 var breadth = 12 } var area = rectangle() print("矩形的面积为 \(area.length*area.breadth)")
The output of the above program execution is:
矩形面积为 72
Construct parameters
You can provide construction parameters when defining constructor init(), as follows:
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 output of the above program execution is:
面积为: 72.0 面积为: 432.0
The name of the internal and external parameters
Like function and method parameters, construct parameters also have an argument name that is used inside the constructor and an external parameter name that is used when the constructor is called.
However, constructors do not have a distinguishable name in parentheses, as functions and methods do. So when you call a constructor, it is primarily through the parameter names and types in the constructor that you want to identify the constructors that need to be called.
If you do not provide an external name for the argument when you define the constructor, Swift automatically generates an external name with the same internal name for each constructor's argument.
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 output of the above program execution is:
red 值为: 1.0 green 值为: 0.0 blue 值为: 1.0 red 值为: 0.5 green 值为: 0.5 blue 值为: 0.5
There are no external name parameters
If you don't want to give an external name to a parameter of the constructor, you can use
_
display the external name that describes it.
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 output of the above program execution is:
面积为: 180.0 面积为: 370.0 面积为: 110.0
Optional property type
If your customized type contains a storage property that logically allows the value to be empty, you need to define it as an optional type option type ( optional property type).
When the storage property is declared optional, it is automatically initialized to empty 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 output of the above program execution is:
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
The constant property is modified during construction
You can modify the value of a constant property at any point in the construction process as long as the value of the constant is determined before the end of the construction process.
For a class instance, its constant properties can only be modified during the construction of the class that defines it;
Although the length property is now a constant, we can still set its value in the constructor of its class:
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 output of the above program execution is:
面积为:Optional(180.0) 面积为:Optional(370.0) 面积为:Optional(110.0)
The default constructor
The default constructor simply creates an instance in which all property values are set to the default:
In the following example, all properties in the ShoppingListItem class have default values, and it is a base class without a parent class, which automatically gets a default constructor 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 output of the above program execution is:
名字为: nil 数理为: 1 是否付款: false
A member constructor for a structure one by one
Structures can automatically get a member-by-member constructor if they provide default values for all storage properties and do not provide custom constructors themselves.
When we call the member-by-member constructor, we complete the initial assignment of the member property by passing the value by passing the same parameter name as the member property name.
The following example defines a structure, Rectangle, that contains two properties, length and breadth. Swift can automatically derive their type Double based on the initial assignments of 100.0 and 200.0 for both properties.
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 storage properties have default values, the structure Rectangle automatically gets a member-by-member constructor init (width:height:). Y ou can use it to create new instances for Rectangle.
The output of the above program execution is:
名字为: nil 矩形的面积: 24.0 矩形的面积: 32.0
The constructor agent for the value type
The constructor can complete part of the instance's construction process by calling other constructors. This process, called a constructor proxy, reduces code duplication between multiple constructors.
In the following example, the Rect structure calls the construction procedures for 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 output of the above program execution is:
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)
Constructor proxy rules
The value type | Class type |
---|---|
Inheritance is not supported, so the constructor proxy process is relatively simple because they can only proxy other constructors that are provided to them. You can use self.init to refer to other constructors that belong to the same value type in a custom constructor. | It can be inherited from other classes, which means that the class has a responsibility to ensure that all of its inherited storage properties are constructed correctly initialized. |
The inheritance and construction process of the class
Swift provides two types of class constructors to ensure that storage properties in all class instances get the initial value, specifying constructors and convenience constructors, respectively.
Specify the constructor | Convenient constructor |
The most important constructor in the class | A secondary, auxiliary constructor in a class |
Initialize all the properties provided in the class, and call the constructor of the parent class up the parent class based on the parent class chain to initialize the parent class. | You can define a convenience constructor to call a specified constructor in the same class and provide default values for its parameters. You can also define a convenience constructor to create an instance of a special purpose or specific input. |
Each class must have at least one specified constructor | Make it easier for class constructors only when necessary |
Init(parameters) { statements } |
convenience init(parameters) { statements } |
Specify the constructor instance
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 output of the above program execution is:
res 为: 10 res 为: 10 res 为: 20
Convenient constructor instance
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 output of the above program execution is:
res 为: 20 res2 为: 30 res2 为: 50
The inheritance and overload of the constructor
Sub-classes in Swift do not inherit the constructor of the parent class by default.
The constructor of the parent class is inherited only in the case of determination and security.
When you override a parent class-specified constructor, 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 output of the above program execution is:
矩形: 4 边 五角型: 5 边
Specify constructors and convenience constructor instances
The next example shows the inheritance of specified constructors, convenience constructors, and auto constructors in the operation.
It defines a class hierarchy that contains two classes, MainClass and SubClass, and will demonstrate how their constructors interact.
class MainClass { var name: String init(name: String) { self.name = name } convenience init() { self.init(name: "[匿名]") } } let main = MainClass(name: "W3CSchool") 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: "W3CSchool") print("MainClass 名字为: \(sub.name)") let sub2 = SubClass(name: "W3CSchool", count: 3) print("count 变量: \(sub2.count)")
The output of the above program execution is:
MainClass 名字为: W3CSchool 没有对应名字: [匿名] MainClass 名字为: W3CSchool count 变量: 3
The failable constructor of the class
If an object of a class, structure, or enumerate type may fail in constructing itself, define a failable constructor for it.
Variable initialization failures can be possible because:
-
Pass in an invalid argument value.
-
Some of the required external resources are missing.
-
No specific conditions were met.
In order to properly handle situations that may fail during this construction process.
You can add one or more failable constructors to a definition of a class, structure, or enumerate type. Its syntax is to add a question mark (init?) after the init keyword.
Instance
In the following example, a structure called Animal is defined, which has a constant property of the String type called species.
At the same time, the structure also defines a failed constructor with a String type parameter species. This failable constructor is used to check whether the incoming argument is an empty string, and if it is an empty string, the failed constructor fails to build the object or 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:
动物初始化为长颈鹿
The failable constructor of the enumerator type
You can get specific enumerator members in an enumerator type by constructing a failable constructor with one or more parameters.
Instance
In the following example, an enumeral type named TemperatureUnit is defined. It contains three possible enumeration members (Kelvin, Celsius, and Fahrenheit) and a failable constructor that is used to find the enumerator members corresponding to the Charter 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:
这是一个已定义的温度单位,所以初始化成功。 这不是一个已定义的温度单位,所以初始化失败。
The failable constructor of the class
A failed constructor of a value type, such as a structure or enumeration type, with no restrictions on when and where the construction failure is triggered.
However, a class's failable constructor can only trigger a failure behavior after all class properties have been initialized and proxy calls between constructors between all classes have occurred.
Instance
In the following example, a class named StudRecord is defined because the studname property is a constant, so once the StudRecord class is constructed successfully, the studname property must have 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:
模块为 失败构造器
Overrides a failable constructor
Like other constructors, you can override the failed constructor of a base class with a failed constructor for a sub-class.
you can also overwrite a base class's failable constructor with a non-fail constructor for a sub-class.
You can override a failable constructor with a non-fail constructor, but the reverse does not work.
A non-failable constructor can never call a failable constructor on begate.
Instance
The following examples describe failable and non-failable constructors:
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]
Fail constructor init!
Typically, we define a failable constructor by adding a question mark (init?) after the init keyword, but you can also define a failable constructor (init!) by adding an exclamation point after the init. H ere's an example:
struct StudRecord { let stname: String init!(stname: String) { if stname.isEmpty {return nil } self.stname = stname } } let stmark = StudRecord(stname: "W3CSchool") if let name = stmark { print("指定了学生名") } let blankname = StudRecord(stname: "") if blankname == nil { print("学生名为空") }
The output of the above program execution is:
指定了学生名 学生名为空