- Published on
struct是值类型,我们可以改变struct实例的属性吗
- Authors
- Name
有这样一道面试题
class Person {
var name: String
init(name: String) {
self.name = name
}
}
struct Company {
var size: Int
var manager: Person
mutating func increaseSize() {
self = Company(size: size + 1, manager: manager)
}
mutating func increaseSizeV2() {
size += 1
}
}
var companyA = Company(size: 100, manager: Person(name: "Peter"))
var companyB = companyA
companyA.size = 150 // if a struct is an immutable value type, why we can mutate the size property?
print(companyA.size) // ? question1
print(companyB.size) // ? question2
companyA.manager.name = "Bob"
print(companyA.manager.name) // ? question3
print(companyB.manager.name) // ? question4
companyA.increaseSize()
print(companyA.size) // ? question5
companyA.increaseSizeV2()
print(companyA.size) // ? question6
定义了两个类型,其中 Person 是 class 类型, Company 是 struct 类型。我们都知道, class 是引用类型, struct 是值类型。 其中有一个问题是这样的, if a struct is an immutable value type, why we can mutate the size property. 这种说法不是完全正确的,In Swift, a struct is a value type, but it is not inherently immutable. Whether a struct is mutate or immutable depends on how it is declared (var or let ) and how its properties and methods are defined. Let's break it down:
- Value Type:
- struct are value types, meaning when you assign a struct to a new variable or pass it to a function, a copy is created. changes to the copy do not affect the original.
- example:
struct Point {
var x: Int
var y: Int
}
var pointA = Point(x:1, y:2)
var pointB = pointA
//copy on write principle , the pointB and pointA is the same instance.
print(pointA)
print(pointB)
// struct isn't supported copy on write ,so the address is deffrent.
pointB.x = 3
print(pointA)
print(pointB)
print(pointA.x)
print(pointB.x)
copy on write 写时复制
Copy-On-Write (COW) 是一种内存管理优化策略,广泛用于编程语言和数据结构中,特别是在 Swift 这样的语言中,用于实现值语义(value semantics)的同时保持性能高效。它通过延迟复制(copy)数据直到需要修改时才进行,从而减少不必要的内存分配和复制开销
在 Swift 中,许多标准库类型(如 Array、Dictionary、String)都使用 COW 来实现值语义。值语义意味着变量的行为类似于独立的值(而不是引用),但在底层,Swift 使用引用类型(如类)来存储数据,通过 COW 优化来避免不必要的复制。
如果想自定义一个类型,支持 COW,需要用到isKnownUniquelyReferenced这个方法来检查引用类型是否唯一引用,即其引用记数是否为1。
class DataStorage {
var data: [Int]
init(data: [Int]) {
self.data = data
}
}
struct MyArray {
private var storage: DataStorage
init(data: [Int]) {
self.storage = DataStorage(data: data)
}
mutating func append(_ value: Int) {
if isKnownUniquelyReferenced(&storage) {
// 唯一引用,直接修改
storage.data.append(value)
} else {
// 共享引用,复制存储
storage = DataStorage(data: storage.data + [value])
}
}
var data: [Int] {
return storage.data
}
}
var array1 = MyArray(data: [1, 2, 3])
var array2 = array1
array1.append(4)
print(array1.data) // [1, 2, 3, 4]
print(array2.data) // [1, 2, 3]
// Create an array
var arrayA = [1, 2, 3]
// Copy the array
var arrayB = arrayA
// Print memory addresses of the underlying buffer
func printBufferAddress<T>(_ array: [T]) {
array.withUnsafeBufferPointer { buffer in
print("Buffer address: \(buffer.baseAddress!)")
}
}
print("Before modification:")
printBufferAddress(arrayA) // Buffer address (shared)
printBufferAddress(arrayB) // Same buffer address as arrayA
// Modify arrayB
arrayB.append(4)
print("\nAfter modification:")
printBufferAddress(arrayA) // Original buffer address
printBufferAddress(arrayB) // New buffer address (copy created)
print("arrayA: \(arrayA)") // [1, 2, 3]
print("arrayB: \(arrayB)") // [1, 2, 3, 4]
isKnownUniquelyReferenced
命名含义: isKnownUniquelyReferenced 检查一个引用类型对象是否被唯一引用。 用处: 主要用于实现复制写时机制,优化性能,避免不必要的复制,特别是在自定义值类型或与引用类型交互时。 典型场景: 自定义数据结构的 COW 实现、与 C API 交互、或需要手动管理内存的场景。
withUnsafePointer(to:_:)
命名含义:提供一个临时的、不安全的指针来访问变量的内存地址,强调操作的局部性和指针的短暂性。 用处:主要用于与 C API 交互、低级内存操作和性能优化场景,特别是在需要将 Swift 变量转换为指针时。 典型场景:调用 C 函数、调试内存地址、处理低级 API 或优化大数据操作。
与 isKnownUniquelyReferenced 的关系:两者都服务于内存管理,但 withUnsafePointer 侧重于指针访问,而 isKnownUniquelyReferenced 侧重于引用计数检查,常用于 COW。