Swift - copy on write

106 阅读7分钟

swift的value type和reference type

讓value type 效能更好的 copy on write

也許有人會想到,value type 每次都要複製,效能應該會比較不好。不過其實不用太擔心,Swift 很聰明,針對某些 value type 它應用 copy on write 的技巧讓效能更好。 像 String型別即運用了 copy on write 的技巧,因此它不會馬上複製,它只在內容修改時才進行複製。以剛剛我們用 struct 定義 Baby,for 迴圈生 3000 個寶寶的例子,以下程式碼並不會讓記憶體成長到七十幾 MB,因為 Baby 的屬性 heyJude 是字串型別,一開始 3000 個寶寶的 heyJude 的內容和 cuteBaby 的heyJude 一樣,所以 3000 個 newBaby的heyJude 可以先讀取 cuteBaby 的heyJude,還不用複製產生新的 heyJude 字串。

understanding-swift-copy-on-write-mechanisms

Value and Reference Types (Apple’s Blog)

So the array2 data is being copied only when we mutate it as you can see in the console print.

代码验证


   var array1: [Int] = [0, 1, 2, 3]
   var array2 = array1

   printZ(address: array1)
   printZ(address: array2)

   //Let's mutate array2 to see what's
   array2.append(4)
   printZ(address: array2)



   func printZ(address o: UnsafeRawPointer ) {

        print(String(format: "%p", Int(bitPattern: o)))

    }
    

打印

0x600001768160

0x600001768160

0x600002626240

浅析Swift中的Copy-on-Write


value types

已深度思考(用时 24 秒)

好的,用户现在询问关于Swift中的“value types”(值类型)。我需要详细解释这个概念,同时结合之前的对话历史。用户之前对Swift术语卡感兴趣,已经举过一些例子,现在特别问到值类型。首先,我得确保自己完全理解值类型在Swift中的定义、特点以及使用场景。

用户之前提到过提升Swift编程能力和英语水平,所以在回答时要结合中英文术语,可能还要提供代码示例和实际应用场景。用户可能希望了解值类型与引用类型的区别,以及为什么在Swift中值类型如此重要,比如结构体和枚举是值类型,而类是引用类型。

考虑到用户之前对术语卡的兴趣,我应该按照之前的四要素结构来组织内容:术语、定义、代码示例、情景句。此外,用户可能需要了解值类型的优缺点,以及何时使用值类型而不是引用类型。可能还需要提到Swift标准库中的值类型,比如数组、字符串、字典等,这些都是结构体实现的。

另外,用户之前提到过在阅读英文文档时遇到困难,所以解释时要尽量清晰,可能用中英对照的方式,同时提供实际代码示例帮助理解。还要注意用户可能存在的常见误区,比如认为值类型总是被复制,实际上Swift在优化时会进行写时复制(copy-on-write),所以性能上并不会差。

可能需要提到内存管理方面的内容,比如值类型存储在栈上,而引用类型在堆上,值类型更安全,因为每个实例都有独立的内存,不会出现意外的共享状态。这也是Swift鼓励使用值类型的原因之一,尤其是在多线程环境中避免数据竞争。

最后,要确保回答结构清晰,分点说明,并给出实际应用建议,比如在需要封装简单数据时使用结构体,需要继承或多态时使用类。同时,结合用户之前的学习方法,比如制作术语卡,可以建议用户如何将值类型的内容整理到自己的知识库中。

以下是关于 Swift 中 Value Types(值类型) 的详细术语卡,结合定义、核心特性、代码示例和应用场景,帮助你深入理解这一关键概念:


术语卡:Value Types(值类型)

1. 定义(Definition)

值类型是一种当被赋值给变量/常量或传递给函数时,会创建独立副本的数据类型。
其核心特征是:每个实例拥有独立的内存空间,修改副本不影响原始值。

2. 核心特性

特性说明
复制行为赋值或传参时生成完整拷贝(但 Swift 优化为写时复制Copy-on-Write
内存位置通常存储在栈(Stack)上,分配/释放更快
线程安全天然线程安全(无共享状态)
Swift 典型代表Struct(结构体)、Enum(枚举)、基本类型(Int, String 等)

3. 代码示例(Code Examples)

基础示例

// 定义结构体(值类型)
struct Point {
    var x: Int
    var y: Int
}

var pointA = Point(x: 1, y: 2)
var pointB = pointA  // 创建副本

pointB.x = 3  // 修改副本不影响原值
print(pointA.x) // 1
print(pointB.x) // 3

对比引用类型(Reference Type)

// 定义类(引用类型)
class Person {
    var name: String
    init(name: String) { self.name = name }
}

let personA = Person(name: "Alice")
let personB = personA  // 共享同一实例
personB.name = "Bob"

print(personA.name) // "Bob"(原值被修改)
print(personB.name) // "Bob"

4. 应用场景(Use Cases)

优先使用值类型的情况

场景理由示例
封装简单数据模型避免不必要的共享状态struct User { var id: Int; var name: String }
需要线程安全多线程环境下无需加锁网络请求的配置参数(struct RequestConfig
函数式编程支持不可变性(Immutable)使用 let 声明值类型变量保证数据不变
高性能需求栈分配更快,且写时复制优化减少内存开销Swift 标准库的 Array, String, Dictionary 均为结构体实现

5. 进阶知识(Advanced Concepts)

写时复制(Copy-on-Write)

  • 机制:Swift 值类型(如 Array)在赋值时不立即复制内存,直到某个副本被修改时才会真正复制
  • 自定义实现
    struct MyArray<T> {
        private var storage: [T] = []
        var count: Int { storage.count }
        
        mutating func append(_ element: T) {
            if !isKnownUniquelyReferenced(&storage) {
                storage = storage.copy()  // 检测到共享时复制
            }
            storage.append(element)
        }
    }
    

值类型与协议(Protocol)

可通过协议扩展增强功能,同时保持值语义:

protocol Drawable {
    func draw()
}

struct Circle: Drawable {
    func draw() { print("Drawing circle") }
}

let shapes: [Drawable] = [Circle(), Circle()]
shapes.forEach { $0.draw() }

6. 情景句(Context Sentences)

  • 开发实践
    "When designing a model for API response data, prefer using struct over class to avoid accidental shared state mutations."
    (设计 API 响应数据模型时,优先使用结构体而非类,避免意外的共享状态修改)

  • 性能优化
    "Swift’s Array uses copy-on-write to optimize performance of value types, making them efficient even with large data sets."
    (Swift 数组通过写时复制优化值类型性能,即使处理大数据集也很高效)

  • 线程安全
    "By passing a struct across threads, we eliminate the risk of data races since each thread gets its own copy."
    (通过跨线程传递结构体,每个线程获得独立副本,从而消除数据竞争风险)


7. 常见误区(Misconceptions)

误区真相
“值类型总是立即深拷贝”Swift 通过写时复制优化,只在必要时拷贝
“值类型不能有方法”结构体和枚举可以定义方法(如 mutating func
“值类型性能一定优于引用类型”小对象更高效,但频繁拷贝大对象可能影响性能(需合理设计)

8. 练习建议(Practice)

  1. 重构代码:将某个 class 改为 struct,观察行为变化
  2. 性能测试:使用 measure 方法对比值类型/引用类型的创建速度
    func testPerformance() {
        measure {
            var points = [Point]()
            for _ in 0..<10_000 {
                points.append(Point(x: 1, y: 2))
            }
        }
    }
    
  3. 探索标准库:查看 ArrayString 的 Swift 源码(GitHub链接),理解其值类型实现

通过理解值类型,你可以写出更安全、高效的 Swift 代码。现在尝试用 struct 重构你的下一个数据模型吧! 🛠️