이번에는 COW라고 흔히 부르는 Copy-on-Write에 대해 알아보겠습니다.
COW는 Swift에서 Collection Type을 복사해서 사용할 때 사용되는데요
예시 코드를 통해 설명해보겠습니다.
우선 이 개념을 확인해보기위해 메모리 주소를 출력해야하는데요!
주소를 출력하기위해 사용된 코드는 밑의 코드를 사용했습니다.
func address(_ o: UnsafeRawPointer) -> String {
let bit = Int(bitPattern: o)
return String(format: "%p", bit)
}
우선 list 하나를 생성하고, 이의 주소값을 출력해보겠습니다.
var list1 = [1,2,3,4,5]
print("list1의 주소:", address(list1))
//결과값: list1의 주소: 0x600002124070
자 이번엔 두번째 리스트를 생성해보고
var list2 = list1
print("list2의 주소:", address(list2))
주소를 출력해보면..??
당연히 다른 주소가 나올 알았지만?
list2의 주소: 0x600002124070
로 출력됩니다.
그럼 이제는 한 번 list2내의 값을 한 번 바꿔볼게요.
list2[0] = 99
print("list2의 주소:", address(list2))
// list2의 주소: 0x600002110b60
짠~ 이제야 주소값이 바뀐 것을 확인해볼 수 있습니다.
그럼 이제 위의 예시를 바탕으로 정리를 좀 해보자면
리스트와 같은 컬렉션 타입의 경우 새 변수에 할당하거나 이를 함수에 전달할 때, 즉시 새 복사본을 생성하지 않는 것입니다.
대신 같은 메모리주소를 공유하여 사용해 메모리를 효율적으로 사용하는 것이죠.
그러다가 값에 변화가 생긴다면 그제서야 새로운 인스턴스를 생성한다~
가 Copy-on-Write에 대한 설명 되겠습니다.
그럼 끝!!!
.
.
.
.
이 아니라 배열까지만 보면 아~ 그렇구나 할 수 있는데요..
그런데..
이것 좀 보시죠..
var dict1 = ["a": 1, "b": 2]
var dict2 = dict1
withUnsafePointer(to: &dict1) { print($0) }
withUnsafePointer(to: &dict2) { print($0) }
dict2["a"] = 10
withUnsafePointer(to: &dict1) { print($0) }
withUnsafePointer(to: &dict2) { print($0) }
/*
0x00000001052f4210
0x00000001052f4218
0x00000001052f4210
0x00000001052f4218
*/
컬렉션 타입 중 딕셔너리에 대한 예시 코드를 적어 보았는데요..
어라..?
여기선 값이 복사되는 순간 주소값이 달라지고, 값이 변경되더라도 이미 달라진 주소값을 유지하는 것을 볼 수 있습니다.
혼란스럽더라구요..
허지만!
좀 딥하게 알아보면
Dictionary와 Set의 경우 컨테이너 객체와 데이터 저장소를 분리하여 관리합니다.
- 컨테이너: Dictionary와 Set은 데이터를 담는 일종의 "포인터" 역할을 하는 컨테이너를 가집니다. 이 컨테이너는 내부적으로 어떤 메모리 위치에 실제 데이터가 저장되어 있는지를 관리하는 구조체
- 데이터 저장소: 실제 데이터가 저장되는 메모리 공간입니다. 여기에는 딕셔너리의 키-값 쌍이나, 세트의 값들이 저장
Dictionary와 Set를 복사할 때, Shallow Copy라는 방식으로 복사됩니다.
Shallow Copy(얕은 복사)란, 실제 데이터는 복사하지않고, 새로운 컨테이너만 할당하는 방식입니다.
즉, 컨테이너만 새로 만들고 데이터 저장소를 공유하는 구조입니다.
var dict1 = ["a": 1, "b": 2]
var dict2 = dict1
의 경우, 얕은 복사가 일어난 것이구요,
새로운 컨테이너를 할당받는 상황입니다. 하지만 실제로는 dict1과 같은 데이터 저장소를 참조하고 있는것이죠.
다시 정리해보면, 두 컨테이너는 서로 다른 메모리 주소를 가지지만, 저장된 데이터는 동일한 메모리 주소를 가리키고 있는 것입니다.
(이제야 좀 이해가 서서히 됩니다!!)
그러면 값 복사시에는 얕은 복사인데, 그럼 값 수정시에는?? Deep Copy!!
수정이 일어날때서야 Swift는 새로운 저장소를 할당하고, 그때서야 기존 데이터를 복사해옵니다.
이를 통해 불필요한 데이터 복사를 방지하고 성능을 최적하시키는 것이죠
여기서 컨테이너와 데이터 저장소를 살펴보면 컨테이너는 단순히 데이터 저장소를 가리키는 포인터 역할을 하고, 이는 수정(Write) 작업이 일어나기 전까지는 공유됩니다.
데이터 저장소는 COW로 인해 수정 작업이 발생할 때 새롭게 할당되며, 이전의 저장소와는 완전히 분리되버립니다.
자 이제 마무리해보면
메모리 주소값이 list와는 다르게 Dic, Set의 경우, 복사했을 때 다르게 보일 수 있다.
하지만!!
이는 그저 list와 Dic,Set의 경우 데이터 저장방식이 다른기에 일어나는 현상일 뿐, COW는 동일하게 적용된다!!
로 정리해볼 수 있을 것 같아요!!
처음 이 개념을 찾아봤을때, 한 줄 읽어보고 딱 이해가 돼서 아 쉽네~ 이랬는데
딥하게 가보니까 새로운 개념도 있고해서 좀 어려웠습니다... 하하
그럼 이번 게시글은 여기~까지~
.
.
.
.
'Swift' 카테고리의 다른 글
[iOS] About ARC(Automatic Reference Counting) (1) | 2024.10.05 |
---|---|
[iOS] About Optional(옵셔널) (0) | 2024.10.01 |
[iOS] About DI, DIP (2) | 2024.09.26 |
[iOS] About Realm (1) | 2024.09.25 |
[iOS] About 메모리 구조 (1) | 2024.09.21 |