为何Set与Dictionary未修改时内存地址不同,而Array却共享?
关于Swift中Set、Dictionary与Array的CoW(写时复制)地址疑惑解答
你搞混了两个完全不同的地址:变量本身在栈上的存储地址,和集合内部指向堆中实际数据的指针地址。
你的代码误区
你用address(of: &arr)打印的是栈上变量的地址——arr和newArr是两个独立的变量,正常情况下它们的栈地址必然不同(你看到Array的栈地址相同大概率是测试环境的特殊优化或者输出误差)。而CoW机制共享的是堆中的实际数据,不是栈上的变量本身。
正确查看CoW共享地址的方式
要验证Set、Dictionary的CoW是否生效,你需要查看它们内部指向堆数据的指针地址,而不是变量的栈地址。以下是修正后的代码:
import Foundation // 打印变量内部存储的堆数据地址 func heapAddress<T>(of value: T) -> String { return withUnsafeBytes(of: value) { bytes in let ptr = bytes.baseAddress!.assumingMemoryBound(to: UnsafeRawPointer.self) return String(format: "%p", ptr.pointee) } } // Array测试 var arr: [Int] = [1,2,3] var newArr = arr print("Array堆地址 - arr: \(heapAddress(of: arr))") print("Array堆地址 - newArr: \(heapAddress(of: newArr))") // 修改前地址相同 newArr.append(4) print("修改后Array堆地址 - newArr: \(heapAddress(of: newArr))") // 修改后地址变化 // Set测试 var setValue: Set<Int> = [1,3,5] var newSetValue = setValue print("\nSet堆地址 - setValue: \(heapAddress(of: setValue))") print("Set堆地址 - newSetValue: \(heapAddress(of: newSetValue))") // 修改前地址相同 newSetValue.insert(7) print("修改后Set堆地址 - newSetValue: \(heapAddress(of: newSetValue))") // 修改后地址变化 // Dictionary测试 var dict: [String: Int] = ["zero":0, "one":1] var newDict = dict print("\nDictionary堆地址 - dict: \(heapAddress(of: dict))") print("Dictionary堆地址 - newDict: \(heapAddress(of: newDict))") // 修改前地址相同 newDict["two"] = 2 print("修改后Dictionary堆地址 - newDict: \(heapAddress(of: newDict))") // 修改后地址变化
原理说明
Swift的Array、Set、Dictionary都是值类型,但它们的内部实现都用了CoW:
- 变量本身存储在栈上,每个独立变量的栈地址必然不同;
- 变量内部持有一个带引用计数的指针,指向堆中的实际数据;
- 当你把一个集合赋值给另一个变量时,只是复制了这个指针,堆数据的引用计数+1,此时两个变量共享同一个堆数据(堆地址相同);
- 当其中一个变量修改数据时,会检查引用计数:如果引用计数>1,就会复制一份堆数据,更新当前变量的指针指向新的堆数据,这就是写时复制的核心。
所以你之前看到Set、Dictionary的变量栈地址不同是完全正常的,这和CoW机制无关——CoW关注的是堆数据的共享,不是栈变量的地址。
内容的提问来源于stack exchange,提问作者홍진표
相关产品推荐
相关产品推荐

