Swift之深入解析构造过程

Posted Serendipity·y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift之深入解析构造过程相关的知识,希望对你有一定的参考价值。

一、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 rectangle 
    // 设置默认值
    var length = 6
    var breadth = 12

var area = rectangle()
print("矩形的面积为 \\(area.length*area.breadth)")
  • 以上程序执行输出结果为:
矩形面积为 72

五、构造参数

  • 可以在定义构造器 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("面积为: \\(ar.area)")

let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面积为: \\(are.area)")
  • 以上程序执行输出结果为:
面积为: 72.0
面积为: 432.0

六、内部和外部参数名

  • 跟函数和方法参数相同,构造参数也存在一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。
  • 然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时,主要通过构造器中的参数名和类型来确定需要调用的构造器。
  • 如果在定义构造器时没有提供参数的外部名字,Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名。
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)")
  • 以上程序执行输出结果为:
red 值为: 1.0
green 值为: 0.0
blue 值为: 1.0
red 值为: 0.5
green 值为: 0.5
blue 值为: 0.5
  • 没有外部名称参数,如果不希望为构造器的某个参数提供外部名字,可以使用下划线_来显示描述它的外部名。
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)")
  • 以上程序执行输出结果为:
面积为: 180.0
面积为: 370.0
面积为: 110.0

七、可选属性类型

  • 如果定制的类型包含一个逻辑上允许取值为空的存储型属性,都需要将它定义为可选类型optional type(可选属性类型)。
  • 当存储属性声明为可选时,将自动初始化为空 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)")
  • 以上程序执行输出结果为:
面积为:Optional(180.0)
面积为:Optional(370.0)
面积为:Optional(110.0)

八、构造过程中修改常量属性

  • 只要在构造过程结束前常量的值能确定,可以在构造过程中的任意时间点修改常量属性的值。
  • 对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
  • 尽管 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)")
  • 以上程序执行输出结果为:
面积为:Optional(180.0)
面积为:Optional(370.0)
面积为:Optional(110.0)

九、默认构造器

  • 默认构造器将简单的创建一个所有属性值都设置为默认值的实例。如下所示,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
  • 结构体的逐一成员构造器,如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。
  • 在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。
  • 下面例子中定义了一个结构体 Rectangle,它包含两个属性 length 和 breadth。Swift 可以根据这两个属性的初始赋值100.0 、200.0自动推导出它们的类型 Double。
struct Rectangle 
    var length = 100.0, breadth = 200.0

let area = Rectangle(length: 24.0, breadth: 32.0)

print("矩形的面积: \\(area.length)")
print("矩形的面积: \\(area.breadth)")
  • 由于这两个存储型属性都有默认值,结构体 Rectangle 自动获得了一个逐一成员构造器 init(width:height:),可以用它来为 Rectangle 创建新的实例。以上程序执行输出结果为:
矩形的面积: 24.0
矩形的面积: 32.0

十、值类型的构造器代理

  • 构造器可以通过调用其它构造器来完成实例的部分构造过程,这一过程称为构造器代理,它能减少多个构造器间的代码重复。
  • 如下所示,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) ")
  • 以上程序执行输出结果为:
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 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。
指定构造器便利构造器
类中最主要的构造器类中比较次要的、辅助型的构造器
初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例
每一个类都必须拥有至少一个指定构造器只在必要的时候为类提供便利构造器
Init(parameters) statements 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) // 初始化超类
    


let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)

print("res 为: \\(res.no1)")
print("res2 为: \\(res2.no1)")
print("res2 为: \\(res2.no2)")
  • 以上程序执行输出结果为:
res 为: 10
res 为: 10
res 为: 20
  • 便利构造器实例
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)")
  • 以上程序执行输出结果为:
res 为: 20
res2 为: 30
res2 为: 50

十二、构造器的继承和重载

  • Swift 中的子类不会默认继承父类的构造器。
  • 父类的构造器仅在确定和安全的情况下被继承。
  • 当重写一个父类指定构造器时,需要写 override 修饰符。
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)")
  • 以上程序执行输出结果为:
矩形: 4 边
五角型: 5
  • 如下所示,在操作中展示指定构造器、便利构造器和自动构造器的继承,它定义了包含两个个类 MainClass、SubClass 的类层次结构,并将演示它们的构造器是如何相互作用的:
class MainClass 
    var name: String
    
    init(name: String) 
        self.name = name
    
    
    convenience init() 
        self.init(name: "[匿名]")
    

let main = MainClass(name: "Runoob")
print("MainClass 名字为: \\(main.name)")

let main2 = MainClass()
print("没有对应名字: \\(main2.name)"数据结构与算法之深入解析“行星碰撞”的求解思路与算法示例

swift学习笔记构造过程

Swift之深入解析可选链的功能和使用

swift 笔记 (十四) —— 构造过程

Swift 构造过程

Swift初见Swift构造过程