基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

Swift中Struct(结构体)和 Class(类)的核心区别

知识点图片

在 Swift 中,Struct(结构体)和 Class(类)虽然在语法上看起来很像(都能定义属性、方法、下标、初始化器),但它们在底层逻辑和内存管理上有着根本的区别。

最核心的区别可以用一句话概括:Struct 是值类型(Value Type),而 Class 是引用类型(Reference Type)。

以下是详细的对比分析:

1. 核心区别:值类型 vs 引用类型

这是两者最大的分水岭。

  • Struct (值类型):

    • 当你将一个结构体赋值给另一个变量,或者传递给函数时,系统会拷贝一份副本。
    • 修改副本不会影响原始实例。
    • 比喻:就像你给同事发了一份 Excel 文件。他在他的电脑上修改了数据,你电脑里的原文件不会变。
  • Class (引用类型):

    • 当你将一个类实例赋值给另一个变量,或者传递给函数时,传递的是内存地址的引用(指针)。
    • 修改新的变量直接影响原始实例,因为它们指向内存中的同一个对象。
    • 比喻:就像你给同事发了一个 Google Sheet(在线文档)的链接。他修改了数据,你打开链接看到的也是修改后的数据。

2. 内存管理 (Stack vs Heap)

  • Struct:

    • 通常分配在栈(Stack)上(除非它是某个 Class 的属性)。
    • 速度快:分配和释放非常高效,不需要复杂的内存管理开销。
    • 线程安全:因为每个线程拿到的都是副本,所以天生具有较高的线程安全性。
  • Class:

    • 总是分配在堆(Heap)上。
    • 速度较慢:需要通过 ARC(自动引用计数)来管理内存。系统需要追踪有多少个变量指向这个对象,当计数为 0 时才销毁。
    • 存在内存泄漏风险(如循环引用),需要小心使用 weakunowned

3. 继承 (Inheritance)

  • Struct: 不支持继承。结构体不能继承另一个结构体。如果你需要复用代码,通常使用协议(Protocol)扩展(Extension)
  • Class: 支持继承。一个类可以继承另一个类的属性和方法。

4. 初始化器 (Initializers)

  • Struct: 编译器会自动赠送一个成员逐一初始化器 (Memberwise Initializer)。你不需要自己写 init 方法。
    plaintext
    struct Point { var x: Int; var y: Int }
    let p = Point(x: 10, y: 20) // 自动生成的
  • Class: 除非所有属性都有默认值,否则你必须手动编写 init 方法。

5. 可变性 (Mutability)

  • Struct:
    • 如果你把结构体实例赋给一个 let 常量,那么它的所有属性(即使属性本身是 var)都不可修改。
    • 在结构体内部修改自身属性的方法,必须标记为 mutating
  • Class:
    • 即使你把类实例赋给一个 let 常量,你依然可以修改它内部的 var 属性。因为 let 只是锁定了指针地址,没锁定指针指向的内容。

6. 身份识别 (Identity)

  • Struct: 只有“相等性”(Equality)。我们关心的是两个结构体的数据是否一样(使用 ==,需遵守 Equatable 协议)。
  • Class: 既有“相等性”,也有“同一性”(Identity)。我们可以判断两个变量是否指向内存中完全同一个对象(使用 === 操作符)。

代码演示核心区别

plaintext
// MARK: - Struct (值类型)
struct HeroStruct {
    var name: String
}

var hero1 = HeroStruct(name: "钢铁侠")
var hero2 = hero1 // 这里发生了拷贝,hero2 是一个新的副本

hero2.name = "蜘蛛侠"

print(hero1.name) // 输出: "钢铁侠" (原件未变)
print(hero2.name) // 输出: "蜘蛛侠"


// MARK: - Class (引用类型)
class HeroClass {
    var name: String
    init(name: String) { self.name = name }
}

var heroA = HeroClass(name: "钢铁侠")
var heroB = heroA // 这里传递的是引用(指针),指向同一个对象

heroB.name = "蜘蛛侠"

print(heroA.name) // 输出: "蜘蛛侠" (原件变了!)
print(heroB.name) // 输出: "蜘蛛侠"

总结:我该用哪一个?

Apple 官方的建议是 默认使用 Struct

选择 Struct 的情况:

  1. 数据结构相对简单,主要目的是封装少量相关的数据(如坐标、尺寸、配置模型)。
  2. 你希望数据被拷贝,而不是被共享。
  3. 不需要继承。
  4. Swift 的标准库(String, Array, Dictionary, Int, Bool)全都是 Struct。

选择 Class 的情况:

  1. 你需要共享可变状态(例如:ViewModel,数据库管理器,网络连接器)。
  2. 你需要继承层级。
  3. 你需要控制对象的生命周期(使用 deinit)。
  4. 你需要与 Objective-C 代码互操作(Objective-C 的对象都是类)。
00:00
00:00