본문 바로가기
iOS📱/Swift

[ Swift ] COW ( Copy-On-Write )

by 텅빈비니 2022. 12. 27.
반응형

안녕하세요 🐶
빈 지식 채우기의 비니🙋🏻‍♂️ 입니다.

오늘은 COW ( Copy-On-Write )에 대해 알아보는 시간을 가지겠습니다.
COW..? 음머머머머

 

1. 개요

Swift에서의 메모리 참조에는 크게 값 타입참조 타입이 있습니다.
그 중 값 타입의 경우는 새로운 인스턴스를 생성하거나 파라미터로 전달될 때 값 복사가 이루어집니다.
다만, 이러한 값 복사 작업은 시간이 상당히 걸리기 때문에 해결방안으로 나온 기술이 바로 COW ( Copy-On-Write ) 입니다.
간단히 말하면, 실제 사용할 때 복사한다! 라는 의미로 자세히 알아보도록 하겠습니다.

2. 예시

2-1. 준비

아래 소스와 같이 주소를 알 수 있는 함수를 정의한 뒤 진행한다.
UnsafeRawPointer 란 간단히 말해 객체의 메모리 주소를 나타내는 타입이다.
func printAddress(object: UnsafeRawPointer) {
    print(object)
}

 

2-2. Array

먼저 Array를 통해 확인해 보도록 하겠습니다.
func testArray() {
    let arr1 = [1, 2, 3, 4, 5]  // (1)
    var arr2 = arr1 // (2)
    
    printAddress(object: arr1)
    printAddress(object: arr2)
    
    arr2[0] = 0 // (3)
    
    printAddress(object: arr2)
}

위에 간단하게 만든 Array의 COW 테스트 소스가 있습니다

  1. arr1 배열 생성
  2. arr2에 arr1을 할당
  3. arr2의 첫 번째 인덱스의 값 변경

Array - COW

위의 결과로 알 수 있듯이 Array 의 경우 COW ( Copy-On-Write ) 가 구현이 되어 있는 것을 볼 수 있다.
왜냐하면 arr2에 arr1이 할당했을 때는 동일한 주소를 바라보고 있으나, arr2의 값이 수정되는 순간 복사가 일어나는 것을 확인할 수 있다.

2-3. Set, Dictionary

다른 Collection 타입인 Set 과 Dictionary도 확인해보겠습니다.
func testSet() {
    var set1: Set<Int> = [1, 2, 3]
    var set2 = set1
    
    printAddress(object: &set1)
    printAddress(object: &set2)
}

func testDictionary() {
    var dic1: Dictionary<Int, Int> = [1:1, 2:2, 3:3]
    var dic2 = dic1
    
    printAddress(object: &dic1)
    printAddress(object: &dic2)
}

Array와 마찬가지로 테스트를 위해 소스를 작성해보았습니다.

Set, Dicionary - COW


엥..? 왜 Array와 다르게 Set, Dictionary는 주소가 다르지.. Colletion 타입은 모두 COW가 구현되어 있다고 했는데..
좀 더 확인해본 결과 Array와 다르게 Set과 Dictionary는 Address Wrapping이 일어나는 것으로 보인다..

응 니 추측이야😢

이 부분에 대해서는 좀 더 공부가 필요할 것으로 보인다.. ( 답 아시는 분은 댓글 남겨주세요 🙏🏻 )

2-4. Struct

대표적인 값 타입으로 정의되는 Sturct로 확인해보도록 하겠습니다.
struct someStruct {
    var a: Int
    var b: Int
}
 
func testStuct() {
    var struct1 = someStruct(a: 1, b: 2)
    var struct2 = struct1
    
    printAddress(object: &struct1)
    printAddress(object: &struct2)
    
    struct1.a = 10
    
    printAddress(object: &struct2)
}

동일하게 Struct도 진행하였다.

Struct - COW

위와 같이 서로 다른 주소 공간에 값들을 가지고 있는 것이 확인되었다.
고로 Struct 구조체는 COW가 구현되어 있지 않다고 알 수 있다.

그렇다면 사용자가 직접 COW 기능을 활용하고 싶을 때는 어떻게 해야 할까?

final class Ref<T> {
    var val : T
    init(_ v : T) {val = v}
}
 
struct Box<T> {
    var ref : Ref<T>
    init(_ x : T) { ref = Ref(x) }
 
    var value: T {
        get { return ref.val } // (1)
        set {
        // 유일하게 참조되는지를 확인
          if (!isUniquelyReferencedNonObjC(&ref)) { // (2)
            ref = Ref(newValue) // (3)
            return
          }
          ref.val = newValue // (4)
        }
    }
}

위와 같이 COW 기능을 직접 구현한 소스를 확인할 수 있다.

  1. Getter의 경우는 참조 데이터를 그대로 반환한다.
  2. Setter의 경우 해당 값이 유일하게 참조되어 있는지를 확인한다.
  3. 아닐 경우, 새로운 참조되는 인스턴스를 생성하여 반환한다.
  4. 맞는 경우, 기존 주소의 있는 값을 수정한다.

 

3. 결론

  • Swift에서 기본적으로 COW가 구현되어 있는 타입은 Collection 프로토콜을 준수하는 Array, Dictionary, Set, String 등이 있다.
  • Struct의 경우 COW 동작이 구현되어 있지 않으므로, 필요하다면 위와 같이 사용자가 직접 구현을 해줘야 한다.

 

이상으로 COW ( Copy-On-Write ) 포스팅을 마치겠습니다.

틀린 부분이나 궁금한 사항은 댓글 남겨주세요~🤭

 


참고

반응형