Swift Array的写时复制

众所周知Swift中Array是值类型,如果其中元素为值类型和引用类型,分别会发生什么?

相关验证方法

检查不同层次的地址

swift 复制代码
// 1. 栈变量地址
withUnsafePointer(to: &array) {
    print("\(name) 栈地址: \($0)")
}

// 2. 堆缓冲区地址
array.withUnsafeBufferPointer {
    print("数组缓冲区地址: \(String(describing: $0.baseAddress))")
}
    
// 3. 元素地址(引用类型时比较)
if let first = array.first as? AnyObject {
    print("\(name)[0] 对象地址: \(ObjectIdentifier(first))")
    }
}

元素为引用类型

随便定义一个类,并创建列表1,然后直接赋值给列表2

swift 复制代码
class Person {
    var name: String
    init(name: String) { self.name = name }
}
var people1 = [Person(name: "Alice"), 
               Person(name: "Bob")]
var people2 = people1

withUnsafePointer打印此时两个数组的栈地址(指向数组的指针)

swift 复制代码
withUnsafePointer(to: &people1) { ptr in
    print("people1 地址: \(ptr)")
}

withUnsafePointer(to: &people2) { ptr in
    print("people2 地址: \(ptr)")

}
// 输出结果
// people1 地址: 0x000000010df001a0
// people2 地址: 0x000000010df001a8

确实是两个不同的数组指针(废话!),但是我们再通过withUnsafeBufferPointer获取数组缓冲区地址

swift 复制代码
people1.withUnsafeBufferPointer { buffer in
    if let baseAddress = buffer.baseAddress {
        print("people1缓冲区地址(堆): \(baseAddress)")
    }
}

people2.withUnsafeBufferPointer { buffer in
    if let baseAddress = buffer.baseAddress {
        print("people2缓冲区地址(堆): \(baseAddress)")
    }
}
// 输出结果
// people1缓冲区地址(堆): 0x000000014d2040c0
// people2缓冲区地址(堆): 0x000000014d2040c0

会发现指向的是同一块缓冲区

如果我们更改people2中元素的name,指针地址和缓冲区地址都没有任何变化(这里就不贴代码和打印结果了),但是如果新增元素

swift 复制代码
people2.append(Person(name: "newPerson"))
withUnsafePointer(to: &people2) { ptr in
    print("people2 地址: \(ptr)")
}

people2.withUnsafeBufferPointer { buffer in
    if let baseAddress = buffer.baseAddress {
        print("people2缓冲区地址(堆):\(baseAddress)")
    }
}
// 输出结果:
// people2 地址: 0x000000010df001a8
// people2缓冲区地址(堆): 0x000000014f404b10

指针地址没变,但是缓冲区地址变了!证明Swift中的数组是写时复制,新开辟了缓冲区。(删除同理)

但是缓冲区里存的是什么?打印下数组中的元素看看

swift 复制代码
/* people1
people1 元素对象地址:
[0]: 0x122b04570
[1]: 0x122b04590

people2 元素对象地址:
[0]: 0x122b04570
[1]: 0x122b04590
[2]: 0x122b05ea0
*/

得出结论。虽然缓冲区确实开了新的,但是引用类型的元素还是不会被复制,相当于只是开了一块新地址存引用类型元素的指针而已。

结论:

  1. Array是值类型
  2. 赋值副本Array时发生逻辑复制(新的数组指针 在栈上),修改副本中的元素也会更改到原Array中的元素
  3. 修复副本Array时才实际复制堆缓冲区

元素为值类型

如果真的能读到值类型,相信也能看懂直接用代码解释了

swift 复制代码
var array1 = ["AAA", "BBB", "CCC"]
var array2 = array1

// 输出结果:

// 栈地址验证,不同
// array1 栈地址: 0x00000001101d0058
// array2 栈地址: 0x00000001101d0060

// 缓冲区 暂时相同
// array1 缓冲区地址: 0x0000000129b04440
// array2 缓冲区地址: 0x0000000129b04440

此时修改元素再查看,array2已经开辟新的缓冲区,就不重复贴新增和删除的代码了,结果也是如此。

swift 复制代码
array2[0] = "new AAA"

// 输出结果:
// array1 缓冲区地址: 0x0000000129b04440
// array2 缓冲区地址: 0x0000000129b0d950

但是!修改了array2并没有像array1那样影响到同一个元素,现在用下面的方法验证下数组中的元素,打印修改后的结果

swift 复制代码
array1.withUnsafeBufferPointer { buffer in
    if let baseAddress = buffer.baseAddress {
        for i in 0..<buffer.count {
            let elementAddress = baseAddress + i
            print("array[\(i)] 地址: \(elementAddress), 值: \(elementAddress.pointee)")
        }
    }
}

array2.withUnsafeBufferPointer { buffer in
    if let baseAddress = buffer.baseAddress {
        for i in 0..<buffer.count {
            let elementAddress = baseAddress + i
            print("array[\(i)] 地址: \(elementAddress), 值: \(elementAddress.pointee)")
        }
    }
}
/* 输出结果:
array[0] 地址: 0x0000000127504170, 值: AAA
array[1] 地址: 0x0000000127504180, 值: BBB
array[2] 地址: 0x0000000127504190, 值: CCC


array[0] 地址: 0x000000012750ba70, 值: newAAA
array[1] 地址: 0x000000012750ba80, 值: BBB
array[2] 地址: 0x000000012750ba90, 值: CCC
*/

元素地址不同,值也不同

小总结

元素类型 值类型 引用类型
赋值 逻辑复制 逻辑复制
缓冲区共享 初始共享 初始共享
元素独立性 完全独立 共享对象
写时复制触发 修改时 修改结构时候(增删)
内存影响 元素复制 只复制指针
相关推荐
njsgcs1 天前
Swift playground 网页刷新切换随机页面的网页查看器WebKit
swift
桃子叔叔3 天前
基于SWIFT框架的预训练微调和推理实战指南之完整实战项目
大模型·swift
菜的不敢吱声3 天前
swift学习第5天
学习·ssh·swift
符哥20083 天前
Swift开发app常见第三方库
学习·swift
初级代码游戏3 天前
iOS开发 SwiftUI 5 : 文本输入 密码输入 多行输入
ios·swiftui·swift
菜的不敢吱声3 天前
swift学习第4天
服务器·学习·swift
菜的不敢吱声5 天前
swift学习第2,3天
python·学习·swift
大熊猫侯佩5 天前
拒绝“假死”:为何上滑关闭是测试大忌?揭秘 iOS 真实 OOM 触发指南
app·swift·apple
大熊猫侯佩5 天前
Swift 6.2 列传(第十六篇):阿朱的“易容术”与阿紫的“毒药测试”
swift·编程语言·apple
麦兜*5 天前
【Swift】苹果App开发全流程解析:从Xcode配置到App Store上架避坑指南
xcode·swift