[Swift] 값타입과 참조타입

값 타입과 참조 타입

https://developer.apple.com/swift/blog/?id=10

Swift의 타입은 크게 두 부류로 나뉘는데, 하나는 “값 타입”으로 각각의 인스턴스가 고유한 데이터의 사본을 가지고 있는 것으로 주로 구조체나 열거타입, 튜플등이 이에 해당된다. 다른 하나는 “참조 타입”으로 이는 각각의 인스턴스가 하나의 데이터 사본을 동시에 참조하는 것으로 클래스의 인스턴스가 여기에 해당한다. 이 글에서는 각 타입의 장점과 언제 어떤 타입을 사용해야 할지를 알아보겠다.

차이점

값타입이 참조타입과 구분되는 가장 큰 특징은 복사 — 대입, 초기화, 파라미터로 전달 –가 되어 개별 이스턴스들이 각각의 독립된 사본으로 존재하게 되는 것이다.

struct S { var data: Int = -1 }
var a = S()
var b = a
a.data = 42
println("\(a.data), \(b.data)") // "42, -1"

레퍼런스를 복사하는 것은 암시적으로는 공유된 인스턴스를 갖는 것이다. 따라서 복사 후에도 두 인스턴스는 동일한 데이터이며, 두 번째 인스턴스를 변경하는 것은 원본에도 영향을 준다.

class C { var data: Int = -1 }
var x = C()
var y = x
x.data = 42
println("\(x.data), \(y.data)") // "42, 42"

변경과 안정성

레퍼런스 타입보다 값 타입을 사용하는 가장 주된 목적은 코드를 이해하기 쉽다는 것이다. 각 인스턴스가 독립된 데이터 사본을 가지고 있다는 것은, 보이지 않는 곳에서 해당 데이터가 임의로 변경되지 않음을 보장할 수 있다는 것이다. 특히 이는 멀티스레드 환경에서 다른 스레드가 예기치 못한 시점에 데이터를 변경해놓을 수 있는 것을 방지한다. 보통 이런 류의 문제는 디버깅이 극히 어렵다.

이 차이점은 값이 변경될 때 두드러지므로, 만약 변경가능한 데이터가 없다면 클래스와 구조체의 차이가 없어진다. 즉 변경이 없으면 값타입이나 참조타입은 기본적으로 동일하다.

만약 클래스가 전적으로 변경불가한 속성을 갖고 있을 때를 가정해보자. 실제로 Swift에서는 읽기만 가능한 프로퍼티를 사용하고, 값을 바꾸는 API를 노출하지 않음으로써 변경 불가한 클래스를 만들 수 있다. Cocoa에서 NSURL이 이런 변경 불가한 클래스이다. 하지만, Swift는 원칙적으로 현재는 클래스를 변경 불가하도록 강제하는 언어적 매커니즘을 가지고 있지 않다. 따라서 완전히 변경 불가한 것을 보장하려한다면 구조체나 열거타입을 사용해야 한다.

선택

새로운 타입을 만들 때 값타입과 참조 타입 중 어떤 것을 선택해야 할까? Cocoa를 사용한다면 많은 API들은 NSObject의 서브 클래스일 것이다. 따라서 class를 사용해야 한다. 다른 경우라면,

  • ==를 사용하여 인스턴스를 비교하는 것이 합당하다면
  • 독립적인 상태로 각각의 사본을 만들고자 한다면
  • 여러 스레드에서 사용될 데이터라면

값 타입을 사용해야 한다. 그리고,

  • ===를 사용하여 인스턴스(포인터)를 비교하는 것이 합당하다면
  • 공유된 상태, 변경이 가능한 상태를 원한다면

클래스를 쓰면 된다.

Swift에서 Array, String, Dictionary는 모두 값 타입이다. 이는 C의 int 타입처럼 순수한 값처럼 동작한다. 특별히 명시적으로 구조 내부를 복사하는 코드를 만들 필요가 없으며, 뒷단에서 다른 코드에 데이터를 불지불식간에 변경하는 일도 없다. 특히 값타입은 동기화 없이 스레드 간에 데이터를 전달할 수 있다. 따라서 안정성을 높이는 관점에서 바라볼 때 이런 모델은 코드를 보다 더 예측가능하게 만들어 줄 것이다.