天天看点

Swift 学习总结六: 构造过程、析构过程

构造过程

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

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

与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

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

存储型属性的初始赋值

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

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

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

  • 创建初始值。
  • 在属性定义中指定默认属性值。
  • 初始化实例,并调用 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 Color {
    let red, green, blue: Double
    //如果你在定义构造器时没有提供参数的外部名字,Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名
    init(red: Double, green: Double, blue: Double) {
        self.red = red
        self.green = green
        self.blue = blue
    }
    
    //提供外部参数
    init(RGBPercentage percentage: Double) {
        red = percentage
        green = percentage
        blue = percentage
    }
    
    //隐藏外部参数
    init(_ r: Double, _ g: Double, _ b: Double) {
        red = r;
        green = g
        blue = b
    }
}


        let color1 = Color(red: 0.8, green: 0.8, blue: 0.8)
        print(color1.red)
        let color2 = Color(RGBPercentage: 0.5)
        print(color2.red)
        let color3 = Color(0.7, 0.7, 0.7)
        print(color3.red)
           

运行结果:

0.8
0.5
0.7
           

默认构造器

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

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

结构体的逐一成员构造器

如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。

我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。

struct zhuYiStruct {
    var length = 200.0;
    var width = 100.0
}

        let zhuYi = zhuYiStruct(length: 24.0, width: 40.0)
        print(zhuYi.width)
           

由于这两个存储型属性都有默认值,结构体 zhuYiStruct 自动获得了一个逐一成员构造器 init(width:height:)。 你可以用它来为 zhuYiStruct 创建新的实例。

以上程序执行输出结果为:

40.0

值类型的构造器代理

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

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

指定构造器 便利构造器
类中最主要的构造器 类中比较次要的、辅助型的构造器
初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。 可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。
每一个类都必须拥有至少一个指定构造器 只在必要的时候为类提供便利构造器
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:50)
    }
}


        let mainC = MainClass(no1: 10)
        print(mainC.no1)
        let subC = SubClass(no1: 20, no2: 30)
        print(subC.no1, subC.no2)
        let subC2 = SubClass(no1: 40)
        print(subC2.no1, subC2.no2)
           

运行结果:

10
20 30
40 50
           

类的可失败构造器

如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器。

变量初始化失败可能的原因有:

  • 传入无效的参数值。
  • 缺少某种所需的外部资源。
  • 没有满足特定条件。

为了妥善处理这种构造过程中可能会失败的情况。

你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面加添问号(init?)。

///结构体可失败构造器
struct Animal {
    let fauna: String
    init?(animal: String) {
        if animal.isEmpty {
            return nil
        }
        fauna = animal
    }
}
///枚举类型的可失败构造器
enum DateEnume {
    case Monday,Tuesday,WensDay
    init?(character: String) {
        switch character {
        case "M":
            self = .Monday
        case "T":
            self = .Tuesday
        case "W":
            self = .WensDay
        default:
            return nil
        }
    }
}
///类的可失败构造器
/*值类型(如结构体或枚举类型)的可失败构造器,对何时何地触发构造失败这个行为没有任何的限制。
但是,类的可失败构造器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后触发失败行为。*/
class GouFailClass {
    let failReason: String
    init?(failR: String) {
        self.failReason = failR
        if failR == "不小心" {
            return nil
        }
    }
}


        ///结构体可失败构造器
        let animal = Animal(animal: "大熊猫")
        if let jieBangAnimal = animal {
            print(jieBangAnimal.fauna)
        }
        ///枚举类型的可失败构造器
        let dateE = DateEnume(character: "S")
        if let JieBangE = dateE {
            print(JieBangE)
        }else{
            print("初始化失败")
        }
        ///类的可失败构造器
        let GouFail = GouFailClass(failR: "不小心")
        if let jieDongFail = GouFail {
            print(jieDongFail.failReason)
        }else{
            print("类初始化失败")
        }

           

运行结果:

大熊猫
初始化失败
类初始化失败
           

可失败构造器 init!

通常来说我们通过在init关键字后添加问号的方式(init?)来定义一个可失败构造器,但你也可以使用通过在init后面添加惊叹号的方式来定义一个可失败构造器(init!)。实例如下

覆盖一个可失败构造器

就如同其它构造器一样,你也可以用子类的可失败构造器覆盖基类的可失败构造器。

者你也可以用子类的非可失败构造器覆盖一个基类的可失败构造器。

你可以用一个非可失败构造器覆盖一个可失败构造器,但反过来却行不通。

一个非可失败的构造器永远也不能代理调用一个可失败构造器。

Swift 析构过程

在一个类的实例被释放之前,析构函数被立即调用。用关键字deinit来标示析构函数,类似于初始化函数用init来标示。析构函数只适用于类类型。

析构过程原理

Swift 会自动释放不再需要的实例以释放资源。

Swift 通过自动引用计数(ARC)处理实例的内存管理。

通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。

例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。

语法

在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号:

deinit {
    // 执行析构过程
}
           

实例:

var counter = 0
class BaseClass {
    init() {
        counter += 1
    }
    deinit {
        counter -= 1
    }
}

        var xiGou: BaseClass? = BaseClass()
        print(counter)
        // 当 xiGou = nil 语句执行后,计算器减去 1,show 占用的内存就会释放。
        xiGou = nil
        print(counter)
           

运行结果: