Swift 初始化
在Swift 4中声明的类、结构体和枚举类型需要进行初始化以准备创建实例。已声明的存储属性会被初始化,新创建的实例的值也会被初始化以继续后续操作。创建初始化函数的关键字是’init()’方法。与Objective-C不同,Swift 4的初始化器不返回任何值。它的作用是在处理之前检查新创建的实例的初始化情况。Swift 4还提供了”去初始化(deinitialization)”过程,用于在实例被释放后执行内存管理操作。
存储属性的初始化器角色
在处理实例之前,存储属性必须对其类和结构体的实例进行初始化。存储属性使用初始化器来分配和初始化值,从而消除了调用属性观察器的需要。初始化器用于存储属性:
- 创建初始值
-
在属性定义内部分配默认属性值
-
为特定数据类型初始化一个实例时,使用’init()’。在init()函数内部不传递任何参数。
语法
init() {
//New Instance initialization goes here
}
示例
struct rectangle {
var length: Double
var breadth: Double
init() {
length = 6
breadth = 12
}
}
var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")
当我们在playground上运行以上程序时,我们得到以下结果-
area of rectangle is 72.0
在这里,结构’rectangle’以Double数据类型初始化了成员length和breadth。Init()方法用于为新创建的成员length和breadth初始化值。通过调用rectangle函数来计算并返回矩形的面积。
通过默认值设置属性值
Swift 4语言提供了Init()函数来初始化存储的属性值。此外,用户可以在声明类或结构成员时默认初始化属性值。当属性在整个程序中都采用相同的值时,我们可以在声明部分中声明它,而不是在init()中进行初始化。通过默认设置属性值,用户可以在为类或结构定义继承时使用。
struct rectangle {
var length = 6
var breadth = 12
}
var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")
当我们使用playground运行上述程序时,我们得到以下结果 –
area of rectangle is 72
在这里,长度和宽度不是在init()中声明,而是在声明时进行初始化。
参数初始化
在Swift 4语言中,用户可以使用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("area is: \(ar.area)")
let are = Rectangle(fromLeng: 36, fromBread: 12)
print("area is: \(are.area)")
当我们在playground中运行上面的程序时,我们得到以下结果:
area is: 72.0
area is: 432.0
本地和外部参数
初始化参数具有与函数和方法参数类似的本地和全局参数名称。本地参数声明用于在初始化体中访问,而外部参数声明用于调用初始化器。Swift 4的初始化器与函数和方法初始化器不同,在调用哪个函数时不会标识使用的是哪个初始化器。
为了解决这个问题,Swift 4引入了init()中每个参数的自动外部名称。这个自动外部名称与在每个初始化参数之前写的本地名称等效。
struct Days {
let sunday, monday, tuesday: Int
init(sunday: Int, monday: Int, tuesday: Int) {
self.sunday = sunday
self.monday = monday
self.tuesday = tuesday
}
init(daysofaweek: Int) {
sunday = daysofaweek
monday = daysofaweek
tuesday = daysofaweek
}
}
let week = Days(sunday: 1, monday: 2, tuesday: 3)
print("Days of a Week is: \(week.sunday)")
print("Days of a Week is: \(week.monday)")
print("Days of a Week is: \(week.tuesday)")
let weekdays = Days(daysofaweek: 4)
print("Days of a Week is: \(weekdays.sunday)")
print("Days of a Week is: \(weekdays.monday)")
print("Days of a Week is: \(weekdays.tuesday)")
当我们在播放器上运行上述程序时,我们会得到以下结果 –
Days of a Week is: 1
Days of a Week is: 2
Days of a Week is: 3
Days of a Week is: 4
Days of a Week is: 4
Days of a Week is: 4
没有外部名称的参数
当不需要给参数指定外部名称时,可以使用下划线 “_” 来覆盖默认行为。
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("area is: \(rectarea.length)")
let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")
let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")
当我们使用 playground 运行以上程序时,我们会得到以下结果−
area is: 180.0
area is: 370.0
area is: 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("area is: \(rectarea.length)")
let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")
let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")
当我们在playground上运行上述程序时,我们会得到以下结果-
area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)
在初始化期间修改常量属性
初始化还允许用户修改常量属性的值。在初始化期间,类属性允许其类实例被超类修改,而不是子类修改。例如,考虑之前的程序中,’length’在主类中被声明为’variable’。下面的程序将变量’length’修改为’constant’变量。
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("area is: \(rectarea.length)")
let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")
let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")
当我们在 playground 上运行上面的程序时,我们得到以下结果-
area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)
默认初始化器
默认初始化器为基类或结构的所有已声明属性提供一个具有默认值的新实例。
class defaultexample {
var studname: String?
var stmark = 98
var pass = true
}
var result = defaultexample()
print("result is: \(result.studname)")
print("result is: \(result.stmark)")
print("result is: \(result.pass)")
在使用Playground运行上述程序时,我们得到以下结果:
result is: nil
result is: 98
result is: true
上述程序的类名定义为’defaultexample’。三个成员函数默认初始化为’studname?’来存储’nil’值,’stmark’初始化为98,’pass’初始化为布尔值’true’。同样,在处理类成员类型之前,可以将类中的成员值初始化为默认值。
结构类型的分成员初始化器
当用户未提供自定义的初始化程序时,Swift 4中的结构类型将自动接收’分成员初始化器’。它的主要功能是使用默认的分成员初始化器初始化新的结构实例,然后将新的实例属性按名称传递给分成员初始化器。
struct Rectangle {
var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)
print("Area of rectangle is: \(area.length)")
print("Area of rectangle is: \(area.breadth)")
当我们在playground上运行上述程序时,我们会得到以下结果-
Area of rectangle is: 24.0
Area of rectangle is: 32.0
结构在初始化时,默认为其成员函数进行初始化,’length’为’100.0’,’breadth’为’200.0’。但是在处理变量length和breadth时,这些值被覆盖为24.0和32.0。
值类型的初始化器委托
初始化器委托被定义为从其他初始化器调用初始化器。它的主要功能是作为可重用性的一种方式,避免在多个初始化器之间重复编写代码。
struct Stmark {
var mark1 = 0.0, mark2 = 0.0
}
struct stdb {
var m1 = 0.0, m2 = 0.0
}
struct block {
var average = stdb()
var result = Stmark()
init() {}
init(average: stdb, result: Stmark) {
self.average = average
self.result = result
}
init(avg: stdb, result: Stmark) {
let tot = avg.m1 - (result.mark1 / 2)
let tot1 = avg.m2 - (result.mark2 / 2)
self.init(average: stdb(m1: tot, m2: tot1), result: result)
}
}
let set1 = block()
print("student result is: \(set1.average.m1, set1.average.m2)
\(set1.result.mark1, set1.result.mark2)")
let set2 = block(average: stdb(m1: 2.0, m2: 2.0),
result: Stmark(mark1: 5.0, mark2: 5.0))
print("student result is: \(set2.average.m1, set2.average.m2)
\(set2.result.mark1, set2.result.mark2)")
let set3 = block(avg: stdb(m1: 4.0, m2: 4.0),
result: Stmark(mark1: 3.0, mark2: 3.0))
print("student result is: \(set3.average.m1, set3.average.m2)
\(set3.result.mark1, set3.result.mark2)")
当我们在playground上运行上述程序时,我们获得以下结果−
(0.0,0.0) (0.0,0.0)
(2.0,2.0) 5.0,5.0)
(2.5,2.5) (3.0,3.0)
初始化委托的规则
简直类型 | 类类型 |
---|---|
不支持对结构体和枚举等值类型的继承。通过self.init引用其他初始化器。 | 支持继承。检查所有存储属性值是否已初始化。 |
类的继承和初始化
类类型有两种初始化方法来检查定义的存储属性是否接收到初始值,即指定初始化器和便利初始化器。
指定初始化器和便利初始化器
指定初始化方法(Designated Initializer) | 便利初始化方法(Convenience Initializer) |
---|---|
被视为类的主要初始化方法 | 被视为类的辅助初始化方法 |
所有类的属性都被初始化,并为进一步初始化调用适当的超类初始化方法 | 指定初始化方法与便利初始化方法结合使用,为特定的用例或输入值类型创建类的实例 |
每个类都至少定义一个指定初始化方法 | 当类不需要初始化方法时,不需要强制定义便利初始化方法。 |
Init(parameters) { statements } | convenience init(parameters) { statements } |
用于指定初始化的程序
class mainClass {
var no1 : Int // local storage
init(no1 : Int) {
self.no1 = no1 // initialization
}
}
class subClass : mainClass {
var no2 : Int // new subclass storage
init(no1 : Int, no2 : Int) {
self.no2 = no2 // initialization
super.init(no1:no1) // redirect to superclass
}
}
let res = mainClass(no1: 10)
let print = subClass(no1: 10, no2: 20)
print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")
当我们在playground上运行上述程序时,我们得到以下结果:
res is: 10
res is: 10
res is: 20
便利初始化程序
class mainClass {
var no1 : Int // local storage
init(no1 : Int) {
self.no1 = no1 // initialization
}
}
class subClass : mainClass {
var no2 : Int
init(no1 : Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// Requires only one parameter for convenient method
override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)
print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")
当我们在playground上运行上面的程序时,我们得到以下结果-
res is: 20
res is: 30
res is: 50
初始化程序的继承和重写
Swift 4默认情况下不允许其子类继承其超类的初始程序以用于其成员类型。继承仅适用于超类初始化程序在某种程度上,将在自动初始化程序继承中讨论。
当用户需要在超类中定义初始化程序时,用户必须定义拥有初始化程序的子类作为自定义实现。当子类必须对超类进行覆盖时,必须声明’override’关键字。
class sides {
var corners = 4
var description: String {
return "\(corners) sides"
}
}
let rectangle = sides()
print("Rectangle: \(rectangle.description)")
class pentagon: sides {
override init() {
super.init()
corners = 5
}
}
let bicycle = pentagon()
print("Pentagon: \(bicycle.description)")
当我们在playground上运行上述程序时,得到以下结果:
Rectangle: 4 sides
Pentagon: 5 sides
在实践中使用指定和便利初始化器
class Planet {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[No Planets]")
}
}
let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")
let noplName = Planet()
print("No Planets like that: \(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)
}
}
当我们在playground上运行上面的程序时,我们会得到以下结果−
Planet name is: Mercury
No Planets like that: [No Planets]
失败的初始化器
在定义类、结构体或枚举值时,当存在任何的初始化失败时,用户必须得到通知。变量的初始化有时会因为以下原因导致失败−
- 无效的参数值。
- 缺少所需的外部资源。
- 条件阻止初始化成功。
为了捕获初始化方法抛出的异常,Swift 4 提供了一个灵活的’失败的初始化器’,用于通知用户在初始化结构体、类或枚举成员时有遗漏。捕获失败的初始化器的关键字是’init?’。此外,不能使用相同的参数类型和名称定义失败的和非失败的初始化器。
struct studrecord {
let stname: String
init?(stname: String) {
if stname.isEmpty {return nil }
self.stname = stname
}
}
let stmark = studrecord(stname: "Swing")
if let name = stmark {
print("Student name is specified")
}
let blankname = studrecord(stname: "")
if blankname == nil {
print("Student name is left blank")
}
当我们在playground中运行上面的程序时,我们得到以下结果−。
Student name is specified
Student name is left blank
枚举的可失败初始化器
Swift 4语言还提供了对枚举的可失败初始化器,以便在枚举成员在初始化值时被遗漏时通知用户。
enum functions {
case a, b, c, d
init?(funct: String) {
switch funct {
case "one":
self = .a
case "two":
self = .b
case "three":
self = .c
case "four":
self = .d
default:
return nil
}
}
}
let result = functions(funct: "two")
if result != nil {
print("With In Block Two")
}
let badresult = functions(funct: "five")
if badresult == nil {
print("Block Does Not Exist")
}
当我们在playground上运行上述程序时,我们得到以下结果-
With In Block Two
Block Does Not Exist
类的可失败初始化器
声明为枚举和结构体的可失败初始化器会在其实现中的任何情况下警告初始化失败。然而,在类中声明的可失败初始化器只有在存储属性被设置为初始值后才会警告失败。
class studrecord {
let studname: String!
init?(studname: String) {
self.studname = studname
if studname.isEmpty { return nil }
}
}
if let stname = studrecord(studname: "Failable Initializers") {
print("Module is \(stname.studname)")
}
当我们在playground中运行上述程序时,我们得到以下结果 –
Module is Optional("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("Planet name is: \(plName.name)")
let noplName = Planet()
print("No Planets like that: \(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)
}
}
当我们使用playground运行上面的程序时,我们得到以下结果 –
Planet name is: Mercury
No Planets like that: [No Planets]
init! 可失败初始化方法
Swift 4 提供 ‘init?’ 来定义一个可选的实例化失败初始化方法。要定义一个特定类型的隐式解包可选的实例,要指定 ‘init!’。
struct studrecord {
let stname: String
init!(stname: String) {
if stname.isEmpty {return nil }
self.stname = stname
}
}
let stmark = studrecord(stname: "Swing")
if let name = stmark {
print("Student name is specified")
}
let blankname = studrecord(stname: "")
if blankname == nil {
print("Student name is left blank")
}
当我们在playground中运行上面的程序时,我们得到以下结果-
Student name is specified
Student name is left blank
必需的初始化函数
为了声明初始化每个子类,必须在init()函数之前定义’required’关键字。
class classA {
required init() {
var a = 10
print(a)
}
}
class classB: classA {
required init() {
var b = 30
print(b)
}
}
let res = classA()
let print = classB()
当我们在playground上运行上述程序时,我们得到以下结果:
10
30
10