깊은복사와 얕은복사는 도대체 뭘까?

hipAn·2022년 9월 23일
0

끄적끄적 성장일지

목록 보기
10/30

깊은복사와 얕은복사는 도대체 무엇을 이야기 하는걸까?

이부분은 전혀 몰라서 열심히 구글링을 해보았다.
(강의자료를 줬는데 왜.. 보질 못하고 구글링만하니..)

결론적으로 알기쉽게 이야기해보자면

깊은 복사(Deep Copy)는 '실제 값'을 새로운 메모리 공간에 복사하는 것을 의미하며,

얕은 복사(Shallow Copy)는 '주소 값'을 복사한다는 의미입니다.


" 응? "....

뭔가 느낌상 a가 x라는곳에 있는데

a를 얕게 복사해서 다른곳에서

a를 부르면 a는 저기 위에 저기에 있다! 라고 주소만 알려주는 느낌이

얕은복사!

깊은복사는 a가 x라는 곳에 있는상태에서
a를 복사하면 a가 저기있어요! 가 아니라 a라는놈이 x의 위치치에 있다! 까지 싹다 복사해서 새로 저장해주는것이라고 생각하면 쉬울거같다.

더 쉽게 이야기해보자면

a = x 라는 값이 메모리에 저장이되어있을때

얕은복사로 a를 불러보면 a는 저~ 기 있어요 하고 위치만 알려주는것이고

깊은복사로 a 를 불러보면 a가 저기있어요~ 하고 위치만 알려주는게아니라

메모리에 새로운 영역을 할당하여 새로 만든것처럼 복사한 a의 정보가 진짜 a랑 똑같이 저장된것이라고 보면 된다.

근데 이렇게만 이야기하면 뭔가 너무 뜬구름 잡는것같이 가벼우니까 확실하게

어느 능력자님의 블로그를 참고자료로 남겨놓도록 하자.

Value and Reference type
모든 데이터 타입은 값 타입(value type) 또는 참조 타입(reference type)을 가진다.

값 타입(Value type) : 각각의 고유의 메모리를 소유한다. 스위프트에서 struct, enum, array, tuples 들이 해당 타입에 속한다.
참조 타입(Reference type) : 생성된 인스턴스들은 주소값을 공유한다. 스위프트에서 class가 해당 타입에 속한다.
두 가지 타입 모두 copy 메소드가 존재하는데 과연 깊은 복사(deep copy)가 일어날까? 아니면 얕은 복사(shallow copy)가 일어날까?

깊은 복사(Deep copy)란?
데이터 자체를 통째로 복사한다.
복사된 두 객체는 완전히 독립적인 메모리를 차지한다.
value type의 객체들은 깊은 복사를 하게 된다.
아래의 예시에서 arr1은 string 배열을 가진다. arr2에 arr1을 할당 해보자.

이때, arr1에 있는 모든 string들은 깊은 복사가 일어나서 새로운 배열을 생성하여 arr2에 할당한다. (String은 value type이므로 깊은 복사가 일어난다.) 때문에 arr1을 변경하여도 arr2에서는 값이 변경되지 않는다.

아래 예시에서는 string1에 string2를 할당한다. 이 후, string1의 값을 변경하면 string2에 값도 변할까?

아니다. String은 깊은 복사가 일어나므로 string1을 변경하여도 string2는 변경되지 않는다.

이렇게 깊은 복사는 인스턴스가 완전히 독립적이다. 이와 같은 개념은 모든 value type에 적용된다.

그런데 만약 value type이 reference type을 포함하고 있는 경우는 어떻게 될까???🤔 이 질문은 잠시 묶혀 두고, 아래에서 살펴보도록 하자.

얕은 복사(Shallow copy)란?
얕은 복사는 아주 최소한만 복사를 한다. 값을 복사한다 하더라도, 인스턴스가 메모리에 새로 생성되지 않는다. 값 자체를 복사하는 것이 아니라 주소값을 복사하여 같은 메모리를 가리키기 때문이다.

새로운 인스턴스를 생성하지 않기 때문에 깊은 복사보다 상대적으로 빠르다. reference type을 복사하는 경우 얕은 복사가 일어난다.

아래 예시를 보자.

class Address {
var address : String

init(_ string : String){
    self.address = string
}

}
a1의 인스턴스를 a2에 할당한다. 이때 앝은 복사가 일어나기 때문에 같은 주소값을 가지게 된다.

만약 a1의 address에 값을 바꾼다면 a2의 address도 같이 바뀌게 된다. 같은 메모리를 차지하고 있기 때문이다.

Reference type의 깊은 복사
reference type의 객체를 복사하면 새로운 객체를 생성하지 않고 항상 주소값이 복사가 된다. 그렇다면 완전히 분리된 객체를 생성할 수는 없을까???

copy() 메소드를 사용하면 참조 타입이더라도 깊은 복사가 이뤄진다.
copy() 메소드는 객체를 반환한다. NSCopying 프로토콜을 채택한 객체는 copy() 메소드를 사용할 수 있다.

위에서 살펴본 Address 객체에 NScopying 프로토콜을 채택하여 깊은 복사를 수행해보자.

class Address : NSCopying {

var address : String

init(_ string : String){
    self.address = string
}
func copy(with zone: NSZone? = nil) -> Any {
    return Address(self.address)
}

}

a1을 복사한 a2객체는 a1 과 독립적인 메모리 주소를 가진다. 새로운 인스턴스를 생성하여 할당하였기 때문이다.

reference type 내부에 reference type을 가지고 있는 경우
예시로 먼저 확인하자.

Person클래스는 내부에 Address 변수를 가지고 있다.

class Person : NSCopying {
var name : String
var city : Address

init(name : String, city: Address){
self.name = name
self.city = city
}
func copy(with zone: NSZone? = nil) -> Any {
return Person(name: self.name, city: self.city)
}
}
person1 객체를 생성하고 copy() 한 값을 person2에 할당 해보자.

var a1 = Address("Seoul")
var person1 = Person(name: "elly", city: a1)
var person2 = person1.copy() as! Person
copy()는 깊은 복사를 하기 때문에 새로운 Person 인스턴스를 생성하여 person2에 할당한다. 그렇다면 내부에 Address는 깊은 복사를 할까? 아니면 얕은 복사를 할까?

신기하게도, Person 내부에 Address에 대해서는 얕은 복사가 이뤄졌다. 만약 person1의 address의 값을 바꾼다면 person2의 address 값도 바뀌게 된다.

reference type을 copy하는 경우 기본적으로 명시하지 않으면 얕은 복사가 일어난다. Person객체에 대해서 명시적으로 깊은 복사를 하도록 해주었지만, 내부에 Address에 대해서는 복사 방식을 명시하지 않았기 때문에 얕은 복사가 이뤄진 것이다.

내부 reference type의 변수에도 깊은 복사를 지정해 주어야 원하는대로 깊은 복사를 수행할 수 있다.

func copy(with zone: NSZone? = nil) -> Any {
return Person(name: self.name, city: self.city.copy() as! Address)
}

Value type 내부에 Reference type을 가지고 있는 경우
아까 궁금했던 질문에 대해 생각해보자.

value type을 안에 reference type을 가지고 있는 경우는 어떨까?

객체의 array, struct나 tuple 안에 reference type을 가지고 있는 경우가 이에 속한다. value type에 경우 copy()를 사용할 수 없다. copy() 메소드는 NSCopying 프로토콜을 채택하여 구현해야 하는데, NSCopying은 NSObject의 서브 클래스에서만 동작한다. value type은 상속을 지원하지 않으며, 때문에 copy()를 사용할 수 없다.

array의 구조는 깊은 복사가 이뤄지지만, 여전히 address는 reference type이므로 얕은 복사가 이뤄진다.

arr1과 arr2는 같은 메모리를 가리키고 있다. 앞서 말했 듯이 reference type은 기본적으로(default) 얕은 복사가 이뤄지기 때문이다.

0개의 댓글