[Swift] Value Type의 메모리 주소

Value Type은 cow를 한다면서 왜 Array만 동일 instance에 대해 같은 주소를 반환할까요?

naljin
7 min readMar 13, 2021

들어가며

흠.. 제목이 딱 맘에 들진 않네유.. 부제에 집중해주세요! (이걸 제목으로 쓰기엔 너무 길었어요 흑흑)

튼 오늘은

Value Type은 cow를 한다면서 왜 Array만 동일 instance에 대해 같은 주소를 반환할까?

에 대한 이야기입니다. 무슨 말이냐고요? 일단 스크롤을 내려봅시다 ㅎㅎ 그럼 고고링~!

의문의 시작

얼마전 Swift의 Type에 대해 공부하다가 OptimizationTips 문서에서 기본 Value Type은 copy on write 최적화를 사용한다는 것을 보았습니다.

All standard library containers in Swift are value types that use COW (copy-on-write) to perform copies instead of explicit copies. In many cases this allows the compiler to elide unnecessary copies by retaining the container instead of performing a deep copy.

대충 Swift에서 Value Type은 할당될때 바로 copy를 하는게 아니라 write가 될 때 찐 copy 를 한다는 얘기임다 ㅇㅇ (copy on write)

직접 확인해보기 위해 변수 a에 [1,2,3] 을 할당하고, 다시 b라는 변수에 a를 할당했습니다. 그리고 각각의 메모리 주소를 출력해봤어요!

func address(of object: UnsafeRawPointer) -> String{
let address = Int(bitPattern: object)
return String(format: "%p", address)
}

//same memory address
var a = [1, 2, 3]
var b = a
address(of: &a) //0x600002b74c20
address(of: &b) //0x600002b74c20

b.append(2)
address(of: &a) //0x600002b74c20
address(of: &b) //0x600001d509d0

결과는 ab 모두 같은 메모리 주소를 출력합니다!! 홀뤼몰뤼~~ 물론 b.append(4)와 같은 변경을 주면 바로 b의 메모리 주소는 바뀌지만요 ㅎㅎ

뚜둔 여기서 그치지 않고 Dictionary, Set, String 등에서도 실험을 해보자구요~~

//String
var c = "1b"
var d = c
address(of: &c) //0x10eaec670
address(of: &d) //0x10eaec680

//Dictionary
var e = ["hi" : 3, "bye" : 3]
var f = e
address(of: &e) //0x10eaec690
address(of: &f) //0x10eaec698

//Set
var g: Set = [1, 2]
var h = g
address(of: &g) //0x10eaec6a0
address(of: &h) //0x10eaec6a8

오잉또잉…..???????? 보이시나요????? 아까와는 다르게 같은 instance 일지라도, 각각 다른 주소를 출력하는게 아니겠어요!?!?!?!

이.. 이런 벱은.. 없는겨..

그래서 이유가 뭐라고요??

흠 침착하고 Stack Over Flow에 파파고의 도움을 받아 질문을 올렸습니다.

제 애완동물입니다

그랬더니 이게 도움이 되겄니?? 하면서 How to prove “copy-on-write” on String type in Swift를 링크로 주더라구요.

따봉 스택오버플로우야 고마워!

역시 세상엔 나와 비슷한 사람이 있었군 ㅎㅎ!

답변을 찬찬히 해석해보자면

func address(of object: UnsafeRawPointer) -> String

이 함수가 Array에 대해서는 같은 값을, String에 대해서는 다른 값을 return 하는 이유는 아래와 같다고 합니다.

  • unsafe pointer를 인자로 받는 함수에 array를 전달하는 것은 첫번째 어레이 인자의 주소를 전달하는 것입니다. 따라서 array들이 같은 저장공간을 사용한다면 동일한 값이 나오게 됩니다.
  • unsafe pointer를 인자로 받는 함수에 string을 전달하는 것은 string을 나타내는 임시 UTF-8 의 pointer를 전달하는 것입니다. 따라서 이 주소는 같은 string일지라도 매 호출마다 달라질 수 있습니다.

😮😮😮😮😮😮😮!!!!! 사실 설명도 완전히 이해하지 못했지만(임시 UTF-8의 포인터라뇨 선생님,,,) 어쨌든 포인트는 type 에 따라 어떤 주소를 전달하는지(?)에 대한 메커니즘이 다른가 보군요 🤔 아니 족굼 어이 없는 부분…

추가

위의 글에서는 실제로 SIL 수준에서 매개변수로 UnsafeRawPointer 타입을 받는 함수에 String을 넘길때 일어나는 과정을 설명한다.

얼렁뚱땅 마무리

답변에서는 String 만 설명해줬지만, 다른 타입들도 비슷하게 동작하기 때문에 다른 주소가 나오는거였겠죠?? ㅎㅎ 일단 흐린 눈하고 이 정도로 넘어가도록 하겄습니당. 아니라면 알려주세요!

사실 저 답변에서는 String의 baseAddress라든지, buffer, ManagedBuffer 등 흥미로운 얘기들이 많이 섞여있어서 Swift 를 까보려고 클론도 받고 조금 살펴 봤는데..!! 할많하않 ◠‿◠ ..ㅎ

뭔가 시도해보려고 했던 흔적들..?ㅋㅎ ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

튼 이렇게 블로그에 써놓은 것에 이외로 유익한 설명들이 추가 되어있으니까 살펴보는걸 추천 드림! 그럼 20000~~

출처

--

--