안녕하세요!
오늘은 Swift에서 쓰는 굉장히 유용한 기능, Copy-on-Write에 대해 알아볼 거예요.
이를 시작하기 전에, Swift에서의 컬렉션 타입부터 먼저 살펴보도록 하죠.
Swift에서 컬렉션 타입은 값 타입(Value Type)이다.
값 타입(Value Type)은 메모리 Stack 영역에 저장되며, 필요시 메모리의 값이 복사되어 전달되고,
참조 타입(Reference Type)은 메모리 Heap 영역에 저장되며, 필요시 메모리 주소가 전달돼요.
Swift에서 컬렉션 타입은 값 타입이라고 했기 때문에 메모리의 값이 복사되어 전달되는 것이죠.
그렇다면 한번 확인을 해볼까요?
var array1 = [1, 2, 3]
var array2 = array1
array2.append(4)
print(array1) // [1, 2, 3]
print(array2) // [1, 2, 3, 4]
위 예시에서 array1 배열을 생성 후 array2 변수에 array1 배열을 할당했어요.
array2에 새로운 값을 추가하고 나서 두 변수를 비교했을 때 값이 다른 게 보이시나요?
메모리의 값이 복사되어 전달되었기 때문에 array2에 새로운 값을 추가해도 array1에는 영향을 미치지 않는 것을 확인할 수 있어요.
컬렉션 타입이 값 타입인 경우 생길 수 있는 문제는??
값 타입은 메모리 Stack 영역에 저장된다고 했죠?.
그렇기 때문에 Heap 영역과는 달리 개발자가 메모리 관리를 따로 하지 않아도 된다는 장점이 있어요.
그런데 컬렉션 타입의 크기는 가변적이에요.
이 말은 컬렉션 타입에 값을 계속해서 추가하면은 그 크기 또한 점점 커진다는 것이죠.
그렇다면 크기가 커진 컬렉션 타입을 복사해서 사용한다면 컬렉션 크기만큼 메모리 사용량도 크게 차지하게 되겠죠??
그래서 Swift에서는 이를 보완하고자 Copy-on-Write라는 메커니즘을 사용하고 있어요.
Copy-on-Write란?
값 타입의 값을 복사하여 전달하려 할 때 실제 값이 바뀌기 전까지는 하나의 메모리 값을 공유해서 사용하는 Swift 언어의 메커니즘.
예시를 보면서 확인해 볼게요.
func address(of object: UnsafeRawPointer) -> String) {
let addr = Int(bitPattern: object)
return String(format: "%p", addr)
}
var array1 = [1, 2, 3]
var array2 = array1
print(address(of: array1)) // 0x600001720060
print(address(of: array2)) // 0x600001720060
array2.append(4)
print(address(of: array1)) // 0x600001720060
print(address(of: array2)) // 0x600002100430
아까의 예시처럼 array1 배열을 만들고 array2 변수에는 array1 배열을 할당했어요.
메모리 주소를 확인해 보면 새로운 변수에 컬렉션 타입인 배열을 전달했는데도 두 변수의 메모리 주소가 동일한 게 보이시죠?
이후 array2의 값을 수정하고 나서 두 변수의 메모리 주소를 비교하면 이전과는 달리 메모리 주소가 달라진 것을 확인할 수 있어요.
어때요!? 꽤 신기하지 않나요??
참고로 Copy-on-Write 메커니즘이 적용되는 타입은 컬렉션뿐 아니라 String 타입에도 적용이 된다고 해요!