Swift 类与结构体

类(class)和结构体(struct)是 Swift 自定义类型的两大主力。它们语法相似,但本质不同——一个是引用类型,一个是值类型。Apple 官方建议"默认用 struct",但 class 仍不可替代。本章讲透它们。

1. struct:值类型,首选

struct 是 Swift 推崇的类型形式。标准库的 Int、String、Array、Dictionary 全是 struct。先看一个完整例子:

// struct:值类型,Swift 默认推荐
struct Point {
    var x: Double
    var y: Double

    // 自动有"成员初始化器"(class 没有)
    // 用 Point(x: 1, y: 2) 创建

    // 计算属性
    var distance: Double {
        return (x * x + y * y).squareRoot()
    }

    // 方法
    func translated(by dx: Double, dy: Double) -> Point {
        return Point(x: x + dx, y: y + dy)
    }

    // 修改 self 的方法必须加 mutating
    mutating func translate(by dx: Double, dy: Double) {
        x += dx
        y += dy
    }
}

var p = Point(x: 3, y: 4)
print(p.distance)                // 5.0

let p2 = p.translated(by: 1, dy: 1)
print(p2)                        // Point(x: 4.0, y: 5.0)

p.translate(by: 10, dy: 10)
print(p)                         // Point(x: 13.0, y: 14.0)

几个要点:

2. 值类型 vs 引用类型(核心区别)

这是 Swift 最关键的设计决策,直接影响代码行为:

// 值类型:赋值即复制
struct Size {
    var width: Double
    var height: Double
}

var a = Size(width: 100, height: 50)
var b = a                        // 复制!
b.width = 200
print(a.width)                   // 100(a 不受影响)
print(b.width)                   // 200

// 对比引用类型
class Box {
    var value = 0
}
let x = Box()
let y = x                        // 复制的是引用!
y.value = 99
print(x.value)                   // 99(x 也变了!)

这条差异决定了何时用谁——下面会总结。

3. class:引用类型,支持继承

// class:引用类型,支持继承
class Animal {
    var name: String

    // 显式初始化器(class 没有自动 init)
    init(name: String) {
        self.name = name
    }

    func speak() -> String {
        return "\(name) 发出声音"
    }

    // 析构器(类似 dealloc)
    deinit {
        print("\(name) 被销毁")
    }
}

// 继承
class Dog: Animal {
    var breed: String

    init(name: String, breed: String) {
        self.breed = breed
        super.init(name: name)   // 必须先初始化本类属性,再调 super.init
    }

    // 重写父类方法
    override func speak() -> String {
        return "\(name) ((breed)) 汪汪叫!"
    }
}

let d = Dog(name: "旺财", breed: "柴犬")
print(d.speak())                 // 旺财 (柴犬) 汪汪叫!

// 多态
let animals: [Animal] = [Animal(name: "x"), Dog(name: "y", breed: "z")]
for a in animals {
    print(a.speak())             // 各自调自己版本
}

class 的特点:

4. 属性的种类

struct 和 class 都可以有四种属性:

struct User {
    var name: String

    // 存储属性(默认值)
    var score: Int = 0

    // 计算属性:每次访问都计算
    var isVip: Bool {
        return score > 100
    }

    // getter + setter
    var scoreInHundreds: Int {
        get { return score / 100 }
        set { score = newValue * 100 }   // newValue 是新值
    }

    // 类型属性(类似 static)
    static let maxScore = 999

    // 属性观察者:willSet / didSet
    var level: Int = 0 {
        willSet { print("即将变为 \(newValue)") }
        didSet { print("从 \(oldValue) 变为 \(level)") }
    }
}

var u = User(name: "小明", score: 50, level: 1)
u.level = 2                       // 触发 willSet/didSet
u.scoreInHundreds = 5
print(u.score)                    // 500

5. Equatable / Hashable / Comparable

Swift 让自定义类型支持判等、哈希、比较,只需声明协议:

// 让 struct 自动支持判等
struct Point2: Equatable, Hashable {
    var x: Double
    var y: Double
    // Equatable 和 Hashable 默认实现会比较所有字段
}

let p1 = Point2(x: 1, y: 2)
let p2 = Point2(x: 1, y: 2)
print(p1 == p2)                  // true
print(p1.hashValue)              // 整数,可作 Set/Dict 的 key

// Comparable:自定义排序
struct Score: Comparable {
    let value: Int
    static func < (lhs: Score, rhs: Score) -> Bool {
        return lhs.value < rhs.value
    }
    // 还需实现 == (来自 Equatable)
}

let s = [Score(value: 3), Score(value: 1), Score(value: 2)]
print(s.sorted())                // [1, 2, 3]

Swift 会自动合成 Equatable/Hashable 的实现(比较所有存储属性)。这让自定义类型可以直接用 ==、作 Set 元素、作字典 key——非常方便。

6. 何时用 struct,何时用 class?

Apple 官方建议,符合以下任一条件用 class,否则用 struct:

其他场合——模型、配置、值对象、几何——都用 struct。Swift 社区 90% 的类型是 struct。

7. 引用计数(ARC)简述

class 实例由 ARC 自动管理引用计数,大多数场合无需操心。但要注意循环引用——两个 class 互相强引用会造成内存泄漏。解决方案:用 weakunowned 打破环:

class Person { var dog: Dog? }
class Dog { weak var owner: Person? }   // weak 打破环

let p = Person()
let d = Dog()
p.dog = d
d.owner = p
// 不会循环引用,d.owner 是 weak

struct 没有这个问题,因为值类型不涉及引用计数——这是 struct 安全的另一个原因。

8. 常见陷阱

小结

struct 是 Swift 首选(值类型,简单安全),class 用于需要继承或引用语义的场景。掌握它们的属性种类、init 规则、Equatable/Hashable 协议,你就能写出整洁的类型系统。下一篇讲 Swift 抽象的另一面——协议

← 上一篇 Swift 字典

下一篇 Swift 协议

✈️💬