Day10 聚焦 Swift 中构建自定义数据类型的核心 ——结构体(Struct)。结构体能将相关的变量和函数 “打包” 成一个新类型(比如用 Album 结构体整合专辑的标题、歌手、年份),是组织代码的基础(Swift 中的 StringIntArray 本质都是结构体)。今天学了结构体的定义、属性(存储属性与计算属性)、方法(含修改自身的 mutating 方法)以及初始化器,整理了实操代码和关键细节,新手朋友跟着练能快速掌握 “自定义类型” 的设计思路~

一、结构体基础:创建自己的数据类型

结构体的核心是 “将多个相关的数据和操作封装成一个新类型”,让代码更具逻辑性和可读性。

1. 定义结构体与使用
// 定义一个 Album 结构体(专辑),包含 3 个存储属性和 1 个方法
struct Album {
    // 存储属性:结构体中存储的数据(常量或变量)
    let title: String    // 专辑标题
    let artist: String   // 歌手
    let year: Int        // 发行年份
    
    // 方法:结构体中的函数,可访问结构体的属性
    func printSummary() {
        print("Title: \(title), Artist: \(artist), Year: \(year)")
    }
}

// 创建结构体实例(类似调用函数,参数对应属性)
let lostRiver = Album(title: "Lost River", artist: "SunJiaxing", year: 2002)

// 访问属性和调用方法
print(lostRiver.title)       // 输出 Lost River
lostRiver.printSummary()     // 输出 Title: Lost River, Artist: SunJiaxing, Year: 2002

  • 命名规范:结构体名首字母大写(如 Album),属性和方法名首字母小写(如 titleprintSummary),这是 Swift 的标准约定(与 StringInt 等系统类型一致)。
  • 初始化器(Initializer):创建结构体实例时,Swift 会自动生成一个 “成员逐一初始化器”,要求为每个属性传入值(如 Album(title:..., artist:..., year:...))。也可以显式调用 init 方法:Album.init(title:..., artist:..., year:...),效果相同。
2. 修改结构体属性的方法:mutating 关键字

如果结构体的方法需要修改自身的属性,必须在方法前加 mutating 关键字(告诉 Swift 这个方法会改变结构体的状态)。

struct Employee {
    let name: String               // 姓名(常量,不可改)
    var vacationRemaining: Int     // 剩余假期(变量,可改)
    
    // 修改属性的方法,必须加 mutating
    mutating func takeVacation(days: Int) {
        if vacationRemaining >= days {
            vacationRemaining -= days  // 修改属性
            print("休假 \(days) 天,剩余 \(vacationRemaining) 天")
        } else {
            print("假期不足!")
        }
    }
}

// 注意:必须用 var 声明结构体实例,才能调用 mutating 方法(let 实例为常量,不可修改)
var archer = Employee(name: "SunJiaxing", vacationRemaining: 14)
archer.takeVacation(days: 5)  // 输出 休假 5 天,剩余 9 天
print(archer.vacationRemaining)  // 输出 9

  • 关键限制:用 let 声明的结构体实例是 “常量实例”,其所有属性(即使是 var 声明的)都不能修改,因此无法调用 mutating 方法(编译时会报错)。

二、结构体的属性:存储属性与计算属性

结构体的属性分两种:存储属性(直接存储数据)和计算属性(动态计算值,不直接存储数据)。

1. 存储属性(Stored Properties)

就是我们前面用 let 或 var 定义的属性,直接存储数据(如 Album 的 titleEmployee 的 name)。

2. 计算属性(Computed Properties)

不直接存储数据,而是通过计算得到值,语法上用 { get set } 定义(get 是读取逻辑,set 是写入逻辑)。

struct Employeee {
    let name: String          // 存储属性:姓名
    var vacationAllocated = 14  // 存储属性:总假期天数
    var vacationTaken = 0       // 存储属性:已休假期
    
    // 计算属性:剩余假期 = 总假期 - 已休假期
    var vacationRemaining: Int {
        get {  // 读取时的逻辑
            vacationAllocated - vacationTaken
        }
        set {  // 写入时的逻辑(newValue 是系统自动提供的参数,代表新值)
            // 当设置剩余假期时,反向计算总假期(假设已休假期不变)
            vacationAllocated = vacationTaken + newValue
        }
    }
}

// 使用计算属性
var aarcher = Employeee(name: "Sun Jiaxing")
aarcher.vacationTaken += 4  // 已休 4 天
print(aarcher.vacationRemaining)  // 读取:14 - 4 = 10

aarcher.vacationRemaining = 5  // 写入:希望剩余 5 天
// 触发 set 逻辑:vacationAllocated = 4(已休) + 5(新剩余) = 9
print(aarcher.vacationAllocated)  // 输出 9(总假期被更新为 9)

  • 特点:计算属性更像 “伪装成属性的函数”,每次访问都会重新计算,适合依赖其他属性动态变化的值。
  • 简化写法:如果只有 get 逻辑(只读计算属性),可以省略 get 关键字:
    var vacationRemaining: Int {
        vacationAllocated - vacationTaken  // 只读,省略 get
    }
    

三、自定义初始化器:控制结构体的创建过程

初始化器(Initializer)是结构体创建实例时的 “构造函数”,负责为所有属性赋值。Swift 会自动生成默认初始化器,但我们也可以自定义,以满足特殊需求(如自动生成部分属性的值)。

1. 默认初始化器(成员逐一初始化器)

结构体未定义自定义初始化器时,Swift 会自动生成一个初始化器,要求为每个属性传入值:

struct Player {
    let name: String
    let number: Int
}

// 使用默认初始化器
let player1 = Player(name: "Megan R", number: 15)
2. 自定义初始化器

可以手动定义初始化器,灵活控制实例的创建(比如部分属性自动生成),但必须保证所有属性在初始化器结束时都有值

struct PlayerCustom {
    let name: String
    let number: Int
    
    // 自定义初始化器:只需要传入姓名,号码自动生成 1-99 的随机数
    init(name: String) {
        self.name = name  // self 代表当前实例,区分参数名和属性名
        number = Int.random(in: 1...99)  // 自动生成号码
    }
}

// 使用自定义初始化器
let player2 = PlayerCustom(name: "SunJiaxing")
print(player2.name)   // 输出 SunJiaxing
print(player2.number) // 输出 1-99 的随机数(如 23)

  • 关键语法
    • 初始化器没有 func 关键字,名称固定为 init
    • 用 self.属性名 区分参数和属性(当参数名与属性名相同时);
    • 必须为所有属性赋值(包括常量属性),否则编译报错。
3. 注意事项:自定义初始化器会覆盖默认初始化器

一旦定义了自定义初始化器,Swift 就不会再生成默认的 “成员逐一初始化器”,除非显式实现:

struct PlayerBothInits {
    let name: String
    let number: Int
    
    // 自定义初始化器1:自动生成号码
    init(name: String) {
        self.name = name
        number = Int.random(in: 1...99)
    }
    
    // 显式实现默认初始化器(成员逐一初始化)
    init(name: String, number: Int) {
        self.name = name
        self.number = number
    }
}

// 两种初始化器都可以用
let player3 = PlayerBothInits(name: "Anna")  // 用自定义初始化器
let player4 = PlayerBothInits(name: "Bob", number: 9)  // 用显式默认初始化器

四、Day10 总结:结构体的核心知识点

概念 作用 关键细节
结构体定义 将相关属性和方法封装成新类型 名首字母大写,属性 / 方法首字母小写
存储属性 直接存储数据 用 let(不可改)或 var(可改)定义
计算属性 动态计算值,不直接存储 用 get(读取)和 set(写入)定义,newValue 代表新值
mutating 方法 修改结构体自身属性的方法 必须加 mutating 关键字,只能被 var 实例调用
初始化器 创建实例时为属性赋值 自定义初始化器必须保证所有属性有值,会覆盖默认初始化器

明日计划

Day11 要学 Swift 的 “类(Class)”—— 与结构体类似,但支持继承(这是面向对象编程的核心)。类和结构体在 Swift 中都很重要,尤其是在 SwiftUI 中,视图多用结构体,而数据模型可能用类。有一起打卡的朋友吗?评论区可以分享你设计的结构体(比如 BookStudent 结构体,包含哪些属性和方法)~

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐