Skip to main content

Swift构造过程

caution

在某个类的子类中,初始化方法里语句的顺序并不是随意的,我们需要保证在当前子类实例的成员初始化完成后才能调用父类的初始化方法:

构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程。这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。

Swift 构造函数使用 init() 方法。

类实例也可以通过定义析构器(deinitializer)在类实例释放之前执行清理内存的工作。

存储型属性的初始赋值

类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。

存储属性在构造器中赋值时,它们的值是被直接设置的,不会触发任何属性观测器。

存储属性在构造器中赋值流程:

  • 创建初始值。
  • 在属性定义中指定默认属性值。
  • 初始化实例,并调用 init() 方法。

构造器

构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名。

语法

init()
{
// 实例化后执行的代码
}

实例

struct rectangle {
var length: Double
var breadth: Double
init() {
length = 6
breadth = 12
}
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")

默认属性值

我们可以在构造器中为存储型属性设置初始值;同样,也可以在属性声明时为其设置默认值。

note

使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型。

struct rectangle {
// 设置默认值
var length = 6
var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")

构造参数

你可以在定义构造器init()时提供构造参数,如下所示:

note

下述代码中实现了函数重载,并且提供了外部参数名。

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)")

没有外部名称参数

如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线_来显示描述它的外部名。

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)")

构造过程中修改常量属性

tip

只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。

对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。

尽管 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)")

默认构造器

默认构造器将简单的创建一个所有属性值都设置为默认值的实例。

以下实例中,ShoppingListItem类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器。

class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()


print("名字为: \(item.name)")
print("数理为: \(item.quantity)")
print("是否付款: \(item.purchased)")

值类型的构造器代理

构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。

以下实例中,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) ")

构造器代理规则

值类型类类型
不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。 你可以使用self.init在自定义的构造器中引用其它的属于相同值类型的构造器。它可以继承自其它类,这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。

类的继承与构造过程

Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。

指定构造器

  • 类中最主要的构造器
  • 初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
  • 每一个类都必须拥有至少一个指定构造器
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)")

便利构造器

  • 类中比较次要的、辅助型的构造器
  • 可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。
  • 只在必要的时候为类提供便利构造器
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)
}
// 便利方法只需要一个参数
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)")