1. 引言
在 Swift 中,结构体(Struct)是构建数据模型和高性能代码的核心工具。与类不同,结构体是值类型,这意味着它在赋值和传递时会被复制,从而避免共享状态带来的复杂性。本文将带你深入理解 Swift 结构体的定义、初始化、属性、方法、扩展及其与类的对比,并通过丰富的代码实例帮助你快速掌握。
2. 结构体的基本定义
使用struct关键字即可定义一个结构体,内部可以声明存储属性:
struct Point { var x: Double var y: Double } let origin = Point(x: 0, y: 0) print(origin.x) // 0.0结构体会自动获得一个“成员逐一初始化器”(Memberwise Initializer),可以按属性名传入初始值。
3. 初始化方法
虽然默认的成员初始化器很方便,但很多时候我们需要自定义初始化逻辑。
struct Rectangle { var width: Double var height: Double // 自定义初始化器 init(squareSide side: Double) { width = side height = side } } let square = Rectangle(squareSide: 10) // square.width 和 square.height 都是 10还可以添加可失败初始化器或带默认值的属性:
struct Car { var brand: String var year: Int init?(brand: String, year: Int) { guard !brand.isEmpty, year > 1885 else { return nil } self.brand = brand self.year = year } }4. 结构体是值类型
结构体赋值或传递给函数时,会进行深拷贝。修改一个变量不会影响另一个:
struct Point { var x = 0, y = 0 } var p1 = Point(x: 1, y: 2) var p2 = p1 // 拷贝 p2.x = 10 print(p1.x) // 1 print(p2.x) // 10而类是引用类型,共享同一实例。这是结构体和类最本质的区别,也是 Swift 推荐优先使用结构体的原因之一。
5. 属性和方法
5.1 存储属性与计算属性
结构体可以拥有存储属性和计算属性,计算属性不直接存储值,而是通过 get/set 提供:
struct Circle { var radius: Double var diameter: Double { get { return radius * 2 } set { radius = newValue / 2 } } }5.2 属性观察器
可以为存储属性添加willSet和didSet:
struct StepCounter { var totalSteps: Int = 0 { willSet { print("将步数设置为 \(newValue)") } didSet { if totalSteps > oldValue { print("增加了 \(totalSteps - oldValue) 步") } } } }5.3 方法
结构体内部可以定义实例方法和类型方法:
struct Vector2D { var x = 0.0, y = 0.0 func length() -> Double { return sqrt(x * x + y * y) } static func unitX() -> Vector2D { return Vector2D(x: 1, y: 0) } }5.4 mutating 方法
默认情况下,结构体实例方法不能修改实例的属性。如果需要修改,必须在方法前添加mutating关键字:
struct Point { var x = 0.0, y = 0.0 mutating func moveBy(dx: Double, dy: Double) { x += dx y += dy } } var point = Point(x: 1, y: 1) point.moveBy(dx: 2, dy: 3) // 现在 point 的坐标为 (3, 4)6. 下标
结构体可以定义下标(subscript),方便通过索引访问内部数据:
struct TimesTable { let multiplier: Int subscript(index: Int) -> Int { return multiplier * index } } let threeTimesTable = TimesTable(multiplier: 3) print(threeTimesTable[6]) // 187. 嵌套类型
结构体内部可以嵌套枚举、结构体或类,形成更清晰的层级结构:
struct ChessBoard { enum Piece { case king, queen, rook, bishop, knight, pawn } struct Square { var row: Int var column: Int var piece: Piece? } var squares: [[Square]] = [] }8. 结构体的扩展
通过扩展(Extension),可以为已有结构体添加新功能,甚至可以扩展 Swift 标准库中的结构体:
extension Point { // 添加计算属性 var isOrigin: Bool { return x == 0 && y == 0 } // 添加方法 func distance(to other: Point) -> Double { let dx = x - other.x let dy = y - other.y return sqrt(dx * dx + dy * dy) } }9. 遵循协议
结构体可以遵循一个或多个协议,例如Equatable、Hashable、Codable,从而获得相等性比较、哈希、编解码等能力:
struct Person: Codable, Equatable { var name: String var age: Int } let alice = Person(name: "Alice", age: 30) let encoder = JSONEncoder() let data = try encoder.encode(alice)10. 结构体与类的对比总结
| 特性 | 结构体 | 类 |
|---|---|---|
| 类型 | 值类型 | 引用类型 |
| 继承 | 不支持 | 支持 |
| 类型转换 | 不支持 | 支持is/as? |
| 析构函数 | 无 | 有deinit |
| 引用计数 | 无 | 有 ARC |
| 可变性 | 实例常量不可变,方法需mutating | 实例常量可修改属性 |
| 使用建议 | 优先用于数据模型、数值类型等 | 需要继承或共享状态时使用 |
11. 实战示例:构建用户信息模型
下面演示如何用结构体构建一个用户管理系统的数据模型,并结合计算属性和 Codable 协议进行数据持久化:
struct Address: Codable { var street: String var city: String var zipCode: String } struct User: Codable, Equatable { var id: Int var name: String var email: String var address: Address var displayName: String { return "\(name) (#\(id))" } static func loadFromDisk() throws -> [User] { let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] .appendingPathComponent("users.json") let data = try Data(contentsOf: url) return try JSONDecoder().decode([User].self, from: data) } }这个模型可以直接用于 JSON 解析和本地存储,充分体现了结构体在 Swift 开发中的便利性。
12. 结语
Swift 结构体是构建安全、高效代码的基石。理解其值语义、初始化器、属性和方法、扩展以及与协议的配合,能帮助你写出更健壮的 iOS 或服务端 Swift 程序。希望本文的代码实例能为你提供实用的参考。