Copy-on-Write(COW)는 값 타입(Value Type)의 복사가 불필요한 경우 실제 데이터를 공유하다가, 변경이 발생할 때 새로운 메모리를 할당하여 복사하는 최적화 기법이다. Swift는 Array
, Dictionary
, Set
등의 컬렉션 타입에서 COW를 활용하여 성능을 개선한다.
Swift에서 값 타입(예: Array
)을 변수에 할당하면 일반적으로 새로운 복사가 일어나지만, 실제로 내부적으로는 동일한 메모리를 참조한다. 이후 값이 변경되는 순간 새로운 메모리를 할당하여 복사를 수행한다.
var array1 = [1, 2, 3] // 새로운 배열 생성
var array2 = array1 // 동일한 메모리 참조
print(array1 === array2) // true (동일한 메모리 참조)
array2.append(4) // 변경이 발생하면 새로운 메모리 할당
print(array1) // [1, 2, 3]
print(array2) // [1, 2, 3, 4]
array1
과 array2
는 초기에는 동일한 메모리를 참조하지만, array2
가 변경되는 순간 새로운 메모리를 할당받아 독립적으로 동작하게 된다.
Swift의 표준 라이브러리는 isKnownUniquelyReferenced(_:)
함수를 사용하여 참조가 유일한지 확인한다. 내부적으로 Array
, Dictionary
등은 클래스 기반의 스토리지(Storage)를 가지고 있으며, 참조 카운트를 확인하여 복사 여부를 결정한다.
import Foundation
class Storage {
var values: [Int]
init(values: [Int]) {
self.values = values
}
}
struct COWArray {
private var storage: Storage
init(values: [Int]) {
self.storage = Storage(values: values)
}
mutating func modifyFirstElement(newValue: Int) {
if !isKnownUniquelyReferenced(&storage) {
print("Copying storage...")
storage = Storage(values: storage.values) // 복사 발생
}
storage.values[0] = newValue
}
func printValues() {
print(storage.values)
}
}
var cowArray1 = COWArray(values: [1, 2, 3])
var cowArray2 = cowArray1 // 동일한 storage 공유
cowArray2.modifyFirstElement(newValue: 100) // 변경 시 복사 발생
cowArray1.printValues() // [1, 2, 3]
cowArray2.printValues() // [100, 2, 3]
이처럼 isKnownUniquelyReferenced(_:)
를 활용하여, 참조가 하나일 경우에만 변경하고 그렇지 않으면 새로운 메모리를 할당하여 복사하는 방식이 COW의 핵심이다.
Swift의 표준 라이브러리에서 COW가 적용되는 대표적인 값 타입은 다음과 같다.
Array
Dictionary
Set
String
이들은 내부적으로 클래스 기반의 저장소를 사용하여 COW를 구현하고 있다.
inout
을 사용하여 불필요한 복사를 방지할 수 있다.Copy-on-Write는 Swift에서 값 타입의 성능을 최적화하기 위한 중요한 기법이다. Swift의 기본 컬렉션 타입들은 COW를 통해 불필요한 메모리 복사를 방지하며, 참조가 변경될 때만 복사가 발생하도록 설계되어 있다. 이를 활용하여 불필요한 복사를 최소화하고 성능을 최적화할 수 있다.