在 Swift 中,结构体(struct)和类(class)是两种核心数据类型,它们的核心差异主要体现在 值类型 vs 引用类型、内存管理方式 和 继承特性 上。以下是详细对比:
一、核心差异总结
| 特性 | 结构体 (struct) | 类 (class) |
|---|---|---|
| 类型 | 值类型 (复制行为) | 引用类型 (指针行为) |
| 内存分配 | 栈内存 (高效) | 堆内存 (需要引用计数) |
| 继承 | 不支持 | 支持 |
| 可变性 | 方法需 mutating 修饰 | 直接修改属性 |
| 线程安全 | 值隔离 (天然安全) | 需手动同步 |
| 默认初始化器 | 自动生成 | 需手动定义或自动生成 |
| 内存地址唯一性 | 每次赋值产生新副本 | 多个变量指向同一实例 |
二、关键差异详解
1. 值类型 vs 引用类型
-
结构体 (值类型)
赋值或传参时会发生值拷贝,修改副本不会影响原始对象。struct Point { var x: Int var y: Int } var p1 = Point(x: 0, y: 0) var p2 = p1 // 值拷贝,p2 是 p1 的独立副本 p2.x = 10 print(p1.x) // 输出 0(原始值未改变) -
类 (引用类型)
赋值或传参时传递内存地址,修改任一变量会影响所有指向该实例的变量。class Dog { var name: String init(name: String) { self.name = name } } let dog1 = Dog(name: "Buddy") let dog2 = dog1 // 传递引用,dog2 和 dog1 指向同一实例 dog2.name = "Max" print(dog1.name) // 输出 "Max"(原始值被修改)
2. 内存管理
-
结构体
分配在栈上,生命周期由作用域决定,无需引用计数,性能更高。 -
类
分配在堆上,通过引用计数(ARC)管理内存,多个引用指向同一实例时会增加计数。class MyClass { var data: Int = 0 } struct MyStruct { var data: Int = 0 } // 类的实例在堆上,结构体实例在栈上 let obj = MyClass() // 引用计数 = 1 let copyObj = obj // 引用计数 = 2
3. 可变性
-
结构体
方法若需修改属性,必须标记为mutating。struct Counter { var count: Int = 0 mutating func increment() { count += 1 // 必须使用 mutating } } -
类
方法可直接修改属性。class Counter { var count: Int = 0 func increment() { count += 1 // 直接修改 } }
4. 继承与多态
-
结构体
不支持继承,但可通过协议 (Protocol) 实现多态。protocol Drawable { func draw() } struct Circle: Drawable { func draw() { print("画圆") } } -
类
支持单继承,可重写方法和属性。class Animal { func speak() { print("动物叫") } } class Cat: Animal { override func speak() { print("喵喵") } }
三、使用场景建议
-
优先选择结构体
- 数据简单、无需共享状态(如坐标
CGPoint、颜色UIColor)。 - 需要线程安全(值类型的独立拷贝避免竞态条件)。
- 适合轻量级数据模型(如 JSON 解析结果)。
- 数据简单、无需共享状态(如坐标
-
选择类
- 需要共享或修改同一实例(如网络请求单例
NetworkManager.shared)。 - 需要继承或重写行为(如 UIKit 中的
UIViewController)。 - 需要与 Objective-C 交互(SwiftUI 中的
@ObservedObject)。
- 需要共享或修改同一实例(如网络请求单例
四、高级示例:内存地址验证
// 结构体 - 每次赋值生成新内存
var struct1 = MyStruct(data: 10)
var struct2 = struct1
withUnsafePointer(to: &struct1) { print("结构体1地址: ($0)") }
withUnsafePointer(to: &struct2) { print("结构体2地址: ($0)") } // 地址不同
// 类 - 共享同一内存
let class1 = MyClass()
let class2 = class1
print("类实例1地址: (Unmanaged.passUnretained(class1).toOpaque())")
print("类实例2地址: (Unmanaged.passUnretained(class2).toOpaque())") // 地址相同
五、总结
- 结构体:轻量、安全、高效,优先用于数据封装。
- 类:灵活、共享、支持继承,适合复杂对象模型。
正确选择类型可以提升代码性能和可维护性。Swift 的标准库(如 Array、String)大量使用结构体设计,体现了值类型的优势。