相关资料:


Struct

通过编写 struct 可以创建自己的自定义复杂数据类型。只需为其命名,然后将结构体的代码放在大括号内即可。

基本声明

//例子:创建名为 Album 的 struct,其中包含 title 和 artist 两个字符串常量,以及整数常量 year。还添加了一个函数来总结三个常量的值。
struct Album {
    let title: String
    let artist: String
    let year: Int
    func printSummary() {
        print("\\(title) (\\(year)) by \\(artist)")
    }
}

//实例化:一个新的 Album 就像调用函数一样 - 只需要按照定义的顺序为每个属性提供值
let red = Album(title: "Red", artist: "Taylor Swift", year: 2012)
let wings = Album(title: "Wings", artist: "BTS", year: 2016)

//red 和 wings 都来自同一个 Album 结构,但是一旦创建它们,它们就像创建两个字符串一样是分开的独立的
print(red.title)
print(wings.artist)

嵌套声明结构

嵌套结构指的是将一个结构放置在另一个结构中。这不会影响项目中的代码,但它有助于保持代码的组织性。如果想象一个具有数百个自定义类型的项目,那么添加这个额外的上下文确实会有帮助

// 当采用嵌套结构,读取子结构时,需要改写成 Mission.CrewRole
struct Mission: Codable, Identifiable {

    struct CrewRole: Codable {
        let name: String
        let role: String
    }

    let id: Int
    let launchDate: Date?
    let crew: [CrewRole]
    let description: String
}

和 Tuple 元组的区别

// 以下元组和结构体具有相同的效果
(name: String, age: Int, city: String)

struct User {
    var name: String
    var age: Int
    var city: String
}

// 使用时对比
func authenticate(_ user: User) { ... }
func showProfile(for user: User) { ... }
func signOut(_ user: User) { ... }

func authenticate(_ user: (name: String, age: Int, city: String)) { ... }
func showProfile(for user: (name: String, age: Int, city: String)) { ... }
func signOut(_ user: (name: String, age: Int, city: String)) { ... }

属性

原本 Swift 中的变量和常量,放到了 struct 里就称为【属性】。struct 有两种属性:存储属性 和 计算属性

//如果 struct 里的属性有默认赋值了...
let name: String
var vacationRemaining = 14

//那实例化时,以下两种方式都是有效的(有默认值的属性可以不填,您可以只填充您需要的部分)
let kane = Employee(name: "Lana Kane")
let poovey = Employee(name: "Pam Poovey", vacationRemaining: 35)

//如果将默认值分配给了【常量属性 let】,则该默认值将从初始值设定项中完全删除。
//要分配默认值,但保留在需要时覆盖它的可能性,请使用【变量属性 var】

存储属性

存储属性:是在 struct 实例内保存一段数据的变量或常量

计算属性

计算属性会在每次访问时,动态计算属性的值。计算属性是“存储属性”和“函数”的混合,它们像存储属性一样访问,但像函数一样工作

// 例子1:我们为该员工分配 14 天的假期,然后根据天数减去它们。虽然可以争取显示剩余假期的天数
// 但这样的方式会丢失有价值的信息:员工最初被授予的天数
struct Employee {
    let name: String
    var vacationRemaining: Int
}

var archer = Employee(name: "Sterling Archer", vacationRemaining: 14)
archer.vacationRemaining -= 5
print(archer.vacationRemaining)    //9
archer.vacationRemaining -= 3
print(archer.vacationRemaining)    //6

// 这时可以改写成计算属性
// 不再将 vacationRemaining 直接分配给它,而是通过从分配的假期数中减去他们已享受的假期数来计算
struct Employee {
    let name: String
    var vacationAllocated = 14
    var vacationTaken = 0
    var vacationRemaining: Int {
        vacationAllocated - vacationTaken
    }
}

// 我们不再将 vacationRemaining 直接分配给它,而是通过从分配的假期数中减去他们已享受的假期数来计算
var archer = Employee(name: "Sterling Archer", vacationAllocated: 14)
archer.vacationTaken += 4         //10
print(archer.vacationRemaining)
archer.vacationTaken += 4         //6
print(archer.vacationRemaining)
// 例子2:通过return返回
struct Wine {
	var age: Int
	var isVintage: Bool
	var price: Int {
		if isVintage {
			return age + 20
		} else {
			return age + 5
		}
	}
}

让计算属性可写入 set

计算属性是由执行函数具体算出来的,那能否直接由我们写入呢?为了解决这个问题,swift 提供了 getter 和 setter 方法区分“可读取”和“可写入”,它们分别代表 “读取时的代码” 和 “写入时的代码” 。

// 当 set 一个计算属性时,其它属性应该怎么变化,这是由我们自行写代码决定的!
// 假设当设置 vacationRemaining 时,我们想改变的是 vacationAllocated,那可以这样写
struct Employee {
    let name: String
    var vacationAllocated = 14
    var vacationTaken = 0
    var vacationRemaining: Int {
        get { vacationAllocated - vacationTaken }
        set { vacationAllocated = vacationTaken + newValue }
    }
}

var archer = Employee(name: "Sterling Archer", vacationAllocated: 14)
archer.vacationTaken += 4
archer.vacationRemaining = 8     //这里 8 对应的就是上面的 newValue
print(archer.vacationAllocated)  //打印 12 (4+8)
        

// 但当设置 vacationRemaining 时,如果我们想改变的是 vacationRemaining,也可以这样写
set { vacationTaken = vacationAllocated - newValue }
        

// 如果逻辑不规划好,甚至会发生“给计算属性赋值后,它重新计算后的值会跟你赋值的值不一致”的情况
// 例如 setter 代码这样写,就算赋值 vacationRemaining = 8 ,访问时也会发现 vacationRemaining = 16,因为它get时又重新计算了
set { vacationAllocated = 20 }

<aside> 💡 请注意 newValue 是由 swift 自动提供的,swift 会存储用户尝试分配给属性的任何值。

</aside>

让计算属性只读 get

属性可以有 getter 和 setter。但当属性没有 setter 时,它被称为“只读”属性。在右边示例中,personType 是计算属性中的【只读属性】。

只读属性可以进行简写:

Screenshot - 2023-09-14 20.12.03.png

惰性属性 lazy

详细说明见: lazy 惰性


属性观察

<aside> 💡 枚举 enum 中不能用属性观察器,在 Swift 中,属性观察通常用于类(class)和结构体(struct)中的属性,而不是枚举(enum)的属性。枚举的成员(case)通常是不具有属性观察的。

</aside>

Swift 支持的属性观察器(property observer),是在属性更改时运行的特殊代码片段。

属性观察器主要有 didSet & willSet 两种形式:

struct App {
    var contacts = [String]() {
        willSet { print("New value will be: \\(newValue)") }
        didSet { print("Old value was \\(oldValue)") }
    }
}

var app = App()
app.contacts.append("Adrian E")
app.contacts.append("Allen W")

大多数情况你会使用 didSet ,因为通常希望在更改发生后采取行动,以便可以更新用户界面、保存更改或执行其他操作。但这不代表 willSet 没有用,只是在实践中它的受欢迎程度明显低于其对应项。

willSet 最常见的使用时间是当您需要在进行更改之前了解程序的状态时。例如,SwiftUI 在某些地方使用 来处理动画,以便它可以在更改之前拍摄用户界面的快照。当它同时具有“之前”和“之后”快照时,它就可以比较两者以查看用户界面中需要更新的所有部分。