一、Swift 中 类(class) 和 结构体(struct) 的区别,以及各自优缺点?
核心区别
特性 | 类(Class) | 结构体(Struct) |
---|---|---|
类型 | 引用类型(传递时共享内存) | 值类型(传递时复制新实例) |
存储位置 | 堆内存 | 栈内存(若包含引用类型属性,其数据仍在堆内存) |
继承 | 支持(除非用 final 修饰) |
不支持 |
类型转换 | 支持运行时类型检查(is /as ) |
仅编译时类型检查 |
内存管理 | 引用计数(ARC) | 自动栈内存释放(无需引用计数) |
方法派发 | 动态派发(运行时确定方法地址) | 静态派发(编译时确定方法地址) |
析构函数 | 支持 deinit |
不支持 |
线程安全 | 需手动处理(共享内存) | 天然线程安全(值隔离) |
赋值行为的本质
-
类(浅拷贝)
赋值时复制指针,新旧变量指向同一内存地址。修改任一变量会影响所有引用。
swiftclass Person { var name: String init(name: String) { self.name = name } } let p1 = Person(name: "Alice") let p2 = p1 p2.name = "Bob" print(p1.name) // 输出 "Bob"
-
结构体(深拷贝)
赋值时复制值,新旧变量独立存储。修改一个不会影响另一个。
swiftstruct Point { var x: Int var y: Int } var p1 = Point(x: 1, y: 2) var p2 = p1 p2.x = 3 print(p1.x) // 输出 1
优缺点分析
类(Class)的优点
- 共享与可变状态:适合需要多个对象共享同一数据的场景(如网络请求管理器)。
- 继承与多态:支持面向对象设计,复用代码逻辑。
- 运行时动态性:支持类型检查、方法重写和 KVO。
类(Class)的缺点
- 内存开销:堆内存分配和引用计数管理增加性能损耗。
- 线程安全隐患:共享内存需额外同步机制(如锁、GCD)。
- 复杂性 :需处理循环引用(
weak
/unowned
)。
结构体(Struct)的优点
- 高性能:栈内存分配速度快,无引用计数开销。
- 线程安全:值隔离特性天然避免共享内存冲突。
- 清晰所有权:深拷贝行为减少意外副作用。
结构体(Struct)的缺点
- 无法继承:不支持继承,复用代码需依赖协议和组合。
- 不适合复杂对象:频繁深拷贝大对象可能导致性能问题。
使用场景建议
场景 | 推荐类型 | 理由 |
---|---|---|
轻量级数据模型(如坐标、颜色) | 结构体 | 值类型更安全高效,无共享风险 |
需要继承或共享状态的组件 | 类 | 利用面向对象特性实现复用和动态行为 |
高频创建的临时对象 | 结构体 | 栈内存快速分配释放,减少堆内存压力 |
需线程安全的数据容器 | 结构体 | 值隔离避免竞态条件 |
补充说明
-
结构体中包含引用类型
若结构体的属性是类实例,深拷贝时仅复制引用(指针),数据仍共享:
swiftstruct Container { var ref: NSMutableArray // 引用类型属性 } var c1 = Container(ref: NSMutableArray()) var c2 = c1 c2.ref.add("Test") // c1.ref 也会被修改
-
协议与扩展
结构体和类均可遵循协议并使用扩展,但结构体无法通过继承实现多态。
-
Copy-on-Write 优化
Swift 标准库中的集合类型(如
Array
、Dictionary
)对结构体实现了写时复制,避免不必要的深拷贝。
官方建议
Apple 在 Swift 官方文档 中明确推荐:
优先使用结构体,除非你需要类独有的特性(如继承、析构函数或引用语义)。
二、 Swift 中什么是可选类型?
1. 什么是可选类型?
可选类型(Optional)是 Swift 中一种特殊的数据类型,用于表示一个变量可能有值(如 Int
、String
等),也可能没有值(nil
)。它的核心设计目的是强制开发者显式处理值缺失的情况,从而避免空指针异常(Null Pointer Exception),提升代码的安全性。
2. 可选类型的语法
-
声明可选类型 :在类型后添加
?
。swiftvar name: String? // 可能为 String 或 nil var age: Int? // 可能为 Int 或 nil
-
隐式解包可选类型 :在类型后添加
!
。swiftvar forcedValue: String! // 声明时允许为 nil,但使用时假设已赋值
3. 可选类型的本质
可选类型是一个泛型枚举,定义如下:
swift
public enum Optional<Wrapped> {
case none // 无值(nil)
case some(Wrapped) // 有值(Wrapped 类型)
}
例如,String?
实际上是 Optional<String>
的简写。
4. 可选类型的解包方式
(1) 强制解包(Force Unwrap)
使用 !
强制解包,但如果值为 nil
会触发运行时错误。
swift
let name: String? = "Alice"
print(name!) // 输出 "Alice"
let age: Int? = nil
print(age!) // 运行时崩溃!
(2) 可选绑定(Optional Binding)
通过 if let
或 guard let
安全解包:
swift
if let unwrappedName = name {
print("Name is (unwrappedName)")
} else {
print("Name is nil")
}
(3) 空合并运算符(Nil-Coalescing Operator)
提供默认值:
swift
let name = optionalName ?? "Unknown"
(4) 可选链式调用(Optional Chaining)
安全访问属性或方法:
swift
let length = user?.name?.count // 若任一环节为 nil,返回 nil
5. 隐式解包可选类型(Implicitly Unwrapped Optional)
- 用途 :用于变量初始化后一定会被赋值,但声明时可能为
nil
的场景(如IBOutlet
)。 - 风险 :若访问时仍为
nil
,会触发运行时错误。
swift
@IBOutlet weak var label: UILabel! // 隐式解包类型
6. 可选类型的优势
- 安全性 :编译器强制处理
nil
情况,减少崩溃风险。 - 清晰性:明确标记可能缺失的值,提升代码可读性。
- 灵活性:与 Swift 的强类型系统结合,支持泛型和模式匹配。
7. 使用场景
-
网络请求结果 :可能成功(有数据)或失败(
nil
)。 -
用户输入处理:如文本框内容可能为空。
-
类型转换 :尝试将
Any
转换为具体类型时可能失败。swiftlet value: Any = "123" let number = value as? Int // Int?
8. 注意事项
- 避免滥用
!
:强制解包应仅在确定值非nil
时使用。 - 优先使用可选绑定 :
if let
和guard let
更安全。 - 合理使用隐式解包:仅用于生命周期明确的变量(如 UI 控件)。
9. 示例代码
swift
// 定义一个可能为 nil 的可选类型
var optionalNumber: Int? = 42
// 安全解包
if let number = optionalNumber {
print("The number is (number)")
} else {
print("The number is nil")
}
// 空合并运算符
let validNumber = optionalNumber ?? 0
// 可选链式调用
struct Person {
var address: Address?
}
struct Address {
var street: String?
}
let person: Person? = Person(address: Address(street: "Main St"))
let street = person?.address?.street // 类型为 String?
10. 总结
可选类型是 Swift 语言的核心特性之一,通过编译时的严格检查,显著提升了代码的健壮性。合理使用可选类型及其解包方式,能有效避免空指针异常,同时使代码逻辑更加清晰可靠。
三、Swift 中什么 是 泛型?
泛型是 Swift 语言中的核心特性之一,允许开发者编写灵活、可重用且类型安全的代码。通过泛型,可以定义适用于多种数据类型的函数、类、结构体或枚举,而无需重复编写相同逻辑的代码。
1. 泛型的基本概念
泛型通过类型参数化 实现,类型参数在定义时作为占位符(如 <T>
),在使用时由具体类型替换。例如:
swift
// 泛型函数:交换任意类型的两个值
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 5, y = 10
swapTwoValues(&x, &y) // T 被推断为 Int
var str1 = "Hello", str2 = "World"
swapTwoValues(&str1, &str2) // T 被推断为 String
2. 泛型的核心作用
- 代码复用:同一套逻辑可处理不同类型的数据。
- 类型安全:编译器在编译时检查类型,避免运行时错误。
- 性能优化:泛型在编译时生成具体类型的代码,无运行时开销。
3. 泛型的使用场景
(1) 泛型函数
适用于需要处理多种类型的算法(如排序、交换):
swift
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
for (index, item) in array.enumerated() {
if item == value { return index }
}
return nil
}
let names = ["Alice", "Bob"]
let index = findIndex(of: "Bob", in: names) // 返回 1
(2) 泛型类型
定义可存储任意类型数据的容器(如自定义集合):
swift
struct Stack<Element> {
private var elements = [Element]()
mutating func push(_ element: Element) {
elements.append(element)
}
mutating func pop() -> Element? {
elements.popLast()
}
}
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
var stringStack = Stack<String>()
stringStack.push("Swift")
(3) 泛型协议
通过 associatedtype
定义协议中的关联类型,允许遵循协议的类型自定义具体类型:
swift
protocol Container {
associatedtype Item
var count: Int { get }
mutating func append(_ item: Item)
subscript(i: Int) -> Item { get }
}
struct IntList: Container {
typealias Item = Int
private var items = [Int]()
var count: Int { items.count }
mutating func append(_ item: Int) {
items.append(item)
}
subscript(i: Int) -> Int { items[i] }
}
4. 泛型约束
限制泛型类型必须满足特定条件(如遵循协议、继承类或实现特定方法):
swift
// 约束 T 必须遵循 Comparable 协议
func maxValue<T: Comparable>(_ a: T, _ b: T) -> T {
return a > b ? a : b
}
// 约束 Key 必须遵循 Hashable,Value 可以是任意类型
struct Dictionary<Key: Hashable, Value> {
private var storage = [Key: Value]()
// ...
}
5. 关联类型(Associated Types)
在协议中使用关联类型,定义泛型需求:
swift
protocol NetworkService {
associatedtype Response: Decodable
func fetchData(completion: @escaping (Result<Response, Error>) -> Void)
}
struct UserService: NetworkService {
typealias Response = User // 指定具体类型
func fetchData(completion: @escaping (Result<User, Error>) -> Void) {
// 网络请求逻辑
}
}
6. 类型擦除(Type Erasure)
处理需要隐藏具体泛型类型的场景(如返回泛型协议的实例):
swift
struct AnyPrinter<T>: Printer {
private let _print: (T) -> Void
init<U: Printer>(_ printer: U) where U.T == T {
_print = printer.print
}
func print(_ value: T) {
_print(value)
}
}
7. 泛型与标准库
Swift 标准库广泛使用泛型,例如:
- 集合类型 :
Array<Element>
、Dictionary<Key, Value>
。 - 可选类型 :
Optional<Wrapped>
。 - 错误处理 :
Result<Success, Failure>
。
8. 泛型的优势
- 减少重复代码:无需为不同类型编写相同逻辑。
- 增强类型安全:编译器在编译时检查类型错误。
- 提升性能:无运行时类型转换开销。
9. 总结
场景 | 示例 | 核心作用 |
---|---|---|
函数逻辑复用 | swapTwoValues<T> |
处理多种类型数据 |
自定义容器 | Stack<Element> |
存储任意类型元素 |
协议抽象 | Container 协议 + 关联类型 |
定义灵活的类型需求 |
类型约束 | T: Equatable |
确保类型满足特定条件 |
通过合理使用泛型,开发者可以显著提升代码的灵活性和健壮性,同时减少冗余代码。
四、 Swift 中的 strong
、weak
和 unowned
详解
1、基本概念
在 Swift 中,strong
、weak
和 unowned
是用于管理对象引用计数的关键字,与 自动引用计数(ARC) 机制密切相关。它们的核心作用是 控制对象生命周期,避免内存泄漏或野指针问题。
关键字 | 行为 | 引用计数 | 是否可为 nil |
安全性 |
---|---|---|---|---|
strong |
默认修饰符,持有对象时增加引用计数,对象不会被释放。 | 增加 | 否(非可选类型) | 安全 |
weak |
不增加引用计数,对象释放后自动置为 nil 。 |
不增加 | 是(可选类型) | 安全(自动置空) |
unowned |
不增加引用计数,对象释放后仍保留悬垂指针,访问时可能导致崩溃。 | 不增加 | 否(非可选类型) | 不安全(需谨慎) |
2、核心区别
(1). strong
-
特点:默认修饰符,持有对象时会增加引用计数,对象生命周期由引用计数控制。
-
适用场景:大多数情况下使用,表示明确的"拥有关系"。
-
示例 :
swiftclass Person { var pet: Dog? // 默认是 strong }
(2). weak
-
特点 :
- 不增加引用计数,对象释放后自动置为
nil
。 - 必须声明为可选类型(
var
+?
)。 - 它在对象释放后弱引用也随即消失。继续访问该对象,程序会得到 nil,不会出现崩溃。
- 不增加引用计数,对象释放后自动置为
-
适用场景 :打破循环引用,尤其用于 非父子关系 的相互引用(如
delegate
)。 -
示例 :
swiftclass ViewController: UIViewController { weak var delegate: DataDelegate? // 弱引用避免循环 }
(3). unowned
-
特点 :
- 不增加引用计数,但假设对象在生命周期内始终有效,不会为
nil
。 - 非可选类型,访问已释放的对象会导致崩溃。
- 不增加引用计数,但假设对象在生命周期内始终有效,不会为
-
适用场景 :两个对象生命周期 严格同步,且被引用对象不会先于引用者释放。
-
示例 :
swiftclass CreditCard { unowned let owner: Customer // Customer 一定比 CreditCard 生命周期长 init(owner: Customer) { self.owner = owner } }
何时使用 unowned
?
1. 适用场景
- 对象生命周期严格绑定 :
被引用对象(如owner
)的生命周期 长于或等于 引用者(如CreditCard
)。 - 避免循环引用且无法使用
weak
:
当需要非可选类型且确保对象不会提前释放时。
2. 使用示例
swift
class Customer {
var card: CreditCard?
}
class CreditCard {
unowned let owner: Customer
init(owner: Customer) {
self.owner = owner
}
}
// 使用
let customer = Customer()
customer.card = CreditCard(owner: customer) // 无循环引用
3. 注意事项
- 绝对不要滥用:必须确保被引用对象不会被提前释放。
- 优先选择
weak
:若无法确保生命周期同步,优先使用weak
。 unowned
与弱引用本质上一样。不同的是,unowned 无主引用 实例销毁后仍然存储着实例的内存地址(类似于OC中的unsafe_unretained), 试图在实例销毁后访问无主引用,会产生运行时错误(野指针)weak
unowned
只能用在 类实例上面weak
、unowned
都能解决 循环引用,unowned
要比weak
性能 稍高
-
- 在生命周期中可能会 变成
nil
的使用weak
- 初始化赋值以后再也不会变成
nil
使用unowned
- 在生命周期中可能会 变成
对比 weak
和 unowned
特性 | weak |
unowned |
---|---|---|
是否可选 | 必须声明为可选类型(? ) |
非可选类型 |
安全性 | 自动置 nil ,访问安全 |
可能触发野指针崩溃 |
生命周期假设 | 允许被引用对象提前释放 | 假设被引用对象始终存在 |
内存管理 | 无需手动置 nil |
需确保引用对象生命周期 |
常见问题
1. 循环引用是如何产生的?
当两个对象通过 strong
相互引用时,引用计数无法归零,导致内存泄漏:
swift
class A {
var b: B?
}
class B {
var a: A?
}
let a = A()
let b = B()
a.b = b // A 强引用 B
b.a = a // B 强引用 A → 循环引用!
2. 如何打破循环引用?
-
将其中一个引用改为
weak
或unowned
:swiftclass B { weak var a: A? // 弱引用打破循环 }
3. 闭包中的循环引用
闭包捕获外部变量时默认是 strong
,需使用捕获列表:
swift
class NetworkManager {
var completion: (() -> Void)?
func fetchData() {
// 使用 [weak self] 避免循环引用
someAsyncTask { [weak self] in
self?.handleData()
}
}
}
总结
场景 | 推荐修饰符 | 理由 |
---|---|---|
默认对象引用 | strong |
明确所有权关系 |
打破父子对象外的循环引用 | weak |
安全自动置空 |
严格同步生命周期的对象引用 | unowned |
避免可选类型解包,需确保对象存活 |
通过合理使用 strong
、weak
和 unowned
,可以避免内存泄漏和野指针问题,写出更健壮的 Swift 代码。
五、Swift 中 static
与 class
关键字对比
核心区别
特性 | static |
class |
---|---|---|
适用类型 | class 、struct 、enum |
仅 class |
修饰存储属性 | ✅ 允许 | ❌ 禁止 |
修饰计算属性/方法 | ✅ 允许 | ✅ 允许 |
是否支持子类重写 | ❌ 不可重写(隐含 final ) |
✅ 可重写(需用 override ) |
协议中的使用 | 协议中统一用 static ,所有类型实现 |
协议中不可用,仅类实现时可选 class |
使用场景
-
static
- 通用场景 :适用于所有类型(
class
、struct
、enum
)。 - 存储属性:定义类级别的常量或变量。
- 禁止重写:明确不希望子类重写的类型方法或属性。
- 协议定义 :协议中声明类型方法或属性时,必须用
static
。
- 通用场景 :适用于所有类型(
-
class
- 类专用 :仅用于
class
类型。 - 允许重写:希望子类重写父类的类型方法或计算属性。
- 计算属性:定义可被子类重写的类计算属性。
- 类专用 :仅用于
示例代码
swift
// 协议定义
protocol MyProtocol {
static func protocolMethod() // 协议中必须用 static
}
// 类实现
class Parent: MyProtocol {
static var storageProperty = "Parent" // 类存储属性
class var computedProperty: String { "Parent" } // 可重写的类计算属性
class func classMethod() { } // 可重写的类方法
static func protocolMethod() { } // 实现协议方法(隐含 final)
}
class Child: Parent {
override class var computedProperty: String { "Child" }
override class func classMethod() { } // ✅ 允许重写
// override static func protocolMethod() { } // ❌ 禁止重写(static隐含final)
}
// 结构体实现
struct MyStruct: MyProtocol {
static func protocolMethod() { } // 结构体必须用 static
}
注意事项
- 存储属性 :
class
不能修饰存储属性(仅static
可以)。 - 协议兼容性 :协议中声明类型方法/属性时,使用
static
;类实现时可用class
或static
(static
会隐含final
)。 - 性能优化 :
static
方法/属性在编译期绑定,效率略高于class
。
六、Swift 访问控制总结
Swift 提供了 5 种访问控制级别,用于限制代码中实体(类、属性、方法等)的可见性,确保代码的封装性和安全性。以下是各访问级别的核心区别及使用场景:
1. 访问级别从高到低
关键字 | 作用范围 | 允许继承/重写 | 适用场景 |
---|---|---|---|
open |
跨模块可见,且允许其他模块继承或重写 | ✅ | 框架或库的公开 API(如 UIKit) |
public |
跨模块可见,但禁止其他模块继承或重写(类和成员默认不可继承) | ❌ | 暴露无需继承的工具类、方法 |
internal |
仅当前模块内可见(默认级别) | - | 模块内部实现细节 |
fileprivate |
仅当前文件内可见 | - | 文件内共享的辅助函数或类型 |
private |
仅当前作用域(类型或扩展)内可见 | - | 类型内部的私有实现细节 |
2. 核心规则
- 默认访问级别 :未显式指定时,默认为
internal
。 - 成员访问限制 :成员的访问级别不能高于其所属类型(如
public class
的private
属性合法,但private class
的public
属性非法)。 - 协议一致性 :遵循协议的类型必须满足协议要求的访问级别(如协议声明为
public
,则遵循类型也需public
)。 - 扩展中的访问控制 :扩展默认继承原始类型的访问级别,但可显式指定(如
private extension
)。
3. 使用示例
框架开发(跨模块)
swift
// Framework 模块
open class NetworkManager { // 允许其他模块继承
public static let shared = NetworkManager()
internal func log() { } // 仅模块内部可见
private var token: String? // 仅当前类可见
}
public struct APIError: Error { // 其他模块可用,不可继承
public let code: Int
}
模块内部
swift
// App 模块
internal struct User { // 默认 internal,仅模块内可见
var name: String
fileprivate var id: Int // 同一文件内可见
}
private extension String { // 扩展内所有成员默认 private
func trimmed() -> String {
self.trimmingCharacters(in: .whitespaces)
}
}
4. 特殊场景
- 单元测试 :通过
@testable import ModuleName
可访问模块的internal
实体。 - 子类重写 :子类方法的访问级别不能低于父类方法(如父类方法为
open
,子类可重写为public
,但不能是internal
)。 - 泛型约束:泛型类型的访问级别需与其类型参数一致或更高。
5. 选择指南
场景 | 推荐级别 | 示例 |
---|---|---|
开发第三方框架或库 | open /public |
公开工具类、核心功能方法 |
模块内部工具类 | internal |
数据模型、网络请求封装 |
文件内共享的辅助函数 | fileprivate |
同一文件内的 JSON 解析工具 |
类型内部私有实现 | private |
缓存属性、敏感数据处理方法 |
通过合理使用访问控制,可以提升代码的可维护性,减少耦合,同时保护关键实现细节不被外部误用。
七、Swift 写时复制(Copy-on-Write)核心总结
1. 核心机制
- 延迟复制 :值类型(如
Array
、String
)在赋值时不立即复制内存,而是共享同一份数据。 - 写入触发:仅当修改副本时,才真正创建新内存,确保原数据不受影响。
2. 实现原理
- 内部引用 :值类型内部通过引用类型(如类)存储实际数据。
- 引用计数检查 :写入前调用
isKnownUniquelyReferenced
,检测数据引用是否唯一:- 唯一引用:直接修改原数据。
- 多引用:创建新副本后再修改,避免共享数据意外变更。
3. 优势
- 性能优化:减少不必要的数据复制,提升内存效率。
- 值类型安全:保留值语义(独立修改),同时兼顾性能。
4. 适用场景
- 高频赋值的大数据:如集合操作、字符串处理。
- 标准库类型 :
Array
、Dictionary
、Set
、String
默认支持 COW。
5. 示例
swift
var a = [1, 2, 3] // 内部引用计数为 1
var b = a // 引用计数 +1(共享数据)
b.append(4) // 检测到多引用,复制新内存再修改(a 仍为 [1,2,3])
总结
COW 通过共享数据 + 写入时复制,平衡了值类型的安全性与性能,是 Swift 高效处理大数据的核心机制。
八、Swift 将集合类型设计为值类型的原因
1. 值类型的核心优势
- 线程安全 :值类型在传递时通过复制确保数据独立性,避免多线程同时修改同一内存。
- 无副作用:方法调用不会意外修改原始数据,代码行为更易推理。
- 内存高效 :结合 写时复制(Copy-on-Write, COW),减少不必要的内存分配。
2. 具体设计动机
(1) 安全性优先
-
避免隐式共享 :引用类型多个变量指向同一内存,修改一处会影响所有引用(如
NSMutableArray
)。 -
值类型赋值即复制 :每个变量持有独立数据,修改副本不影响原数据。
swiftvar a = [1, 2, 3] var b = a b.append(4) print(a) // [1, 2, 3](a 未被修改)
(2) 性能优化
-
栈内存分配:值类型默认在栈上分配,操作高效(指针移动即可)。
-
写时复制(COW) :延迟实际内存复制,仅在修改时创建新副本。
swiftvar largeArray = [Int](repeating: 0, count: 1000000) var copy = largeArray // 不立即复制内存(共享存储) copy[0] = 1 // 修改时触发复制(独立存储)
(3) 不可变性的真正保证
-
let
语义严格 :值类型声明为let
后,内容完全不可变。 -
引用类型的缺陷 :即使使用
let
修饰引用类型(如NSArray
),内部数据仍可能被修改。swiftlet nsArray: NSArray = NSMutableArray() let swiftArray = [1, 2, 3] // nsArray 可被其他引用修改(不安全),swiftArray 完全不可变
3. 与引用类型的对比
特性 | 值类型(Array/String/Dictionary) | 引用类型(NSArray/NSString) |
---|---|---|
内存分配 | 栈 + COW 优化堆内存 | 堆内存 |
线程安全 | 天然安全(独立内存) | 需手动同步(如锁、GCD) |
赋值行为 | 深拷贝(COW 延迟实际复制) | 浅拷贝(共享内存) |
不可变性 | let 完全不可变 |
let 仅保证引用不变,内容可能被其他引用修改 |
4. 写时复制(COW)的实现原理
-
内部引用类型存储 :值类型内部使用类(如
_ContiguousArrayStorage
)存储实际数据。 -
引用计数检测 :通过
isKnownUniquelyReferenced
检查数据是否被多引用。 -
按需复制 :修改数据时,若引用非唯一,则创建新副本再修改。
swiftstruct MyArray<T> { private var storage: Storage<T> // 内部引用类型 mutating func append(_ element: T) { if !isKnownUniquelyReferenced(&storage) { storage = storage.copy() } storage.append(element) } }
5. 总结
- 安全第一:值类型 + COW 解决了引用类型在多线程和共享数据中的安全隐患。
- 性能平衡:栈内存和 COW 机制将复制开销降至最低。
- 开发友好:严格的不可变性和可预测行为,减少代码副作用。
通过将 Array
、String
、Dictionary
设计为值类型,Swift 在内存效率、线程安全和代码可靠性之间取得了最佳平衡,同时借助 COW 技术避免了传统值类型的性能缺陷。