Swift - 값(Value)과 레퍼런스(Reference)

애플의 공식 스위프트(Swift) 블로그에서 재미있는 내용이 올라와서 이를 설명해보는 글을 작성해본다. 참고로 글 내용은 값과 레퍼런스의 차이에 대한 것인데, 이 덕분에 클래스(class)와 구조체(struct)의 차이점을 한 가지 더 알 수 있게 되었다. 이 글에서는 이 중 값과 레퍼런스에 대한 부분만 정리한다.

값(Value) 이라는 건 정말 수치 등의 순수한 데이터 그 자체를 의미한다.
// Value

var a = 10
var b = 5

a = b       // a = 5, b = 5

b = 999
a           // 5
b           // 999
위 예제에서 a와 b는 Int 형식의 변수로 선언되어서 값을 넣고 다른 변수의 값을 대입해 보고 다시 값을 바꿔보는데, a와 b는 별개의 값을 담고 있다는 것을 확실하게 알 수 있다. 물론 이건 사람에게 당연한 사고방식이라고 생각된다.

하지만 스위프트에서는 변수나 상수 등의 심볼에 값을 대입하는데 있어 값(Value) 뿐만 아니라 레퍼런스(Reference) 개념을 가지고 있다. C의 포인터와 비슷한 이 개념은 값 자체를 가지는게 아니라 값을 보관하고 있는 메모리 포인터를 가지고 있다고 볼 수 있다.
// Reference

class SomeClass {
    var value = 0
    
    init(value: Int) {
        self.value = value
    }
}

var obj1 = SomeClass(value: 10)
var obj2 = SomeClass(value: 20)

obj1.value          // 10
obj2.value          // 20

obj1 = obj2
obj1.value          // 20
obj2.value          // 20

obj2.value = 100
obj1.value          // 100
obj2.value          // 100
새로운 클래스인 SomeClass를 정의하고 이 클래스의 인스턴스를 만들어서 앞서 본 예제와 비슷한 식으로 써 봤다. 하지만 결과는 다르다.

obj1 = obj2라는 코드가 실행된 이후 obj1과 obj2는 마치 하나의 오브젝트 처럼 동작한다. 이는 obj1 = obj2라는 코드가 '레퍼런스 복사'로 동작한다는 것을 의미한다. 즉 obj1과 obj2는 같은 메모리를 가리키게 되는 것이다.

그래서 이후 obj1이나 obj2의 값을 바꾸게 되면 동일하게 다른 쪽의 값도 바뀌게 된다. 어차피 동일한 메모리의 내용이 바뀌는 것이니까.

대부분의 OOP 언어에서도 비슷한 개념을 사용하기 때문에 이런 언어들을 숙지하고 있다면 스위프트의 방식도 익숙할 것이다.

하지만 스위프트의 구조체(struct)는 뭔가 다르다

이번에는 클래스가 아닌 구조체(struct)의 경우를 보자.
// Structure Instance is The Value

struct SomeStruct {
    var value = 0
    
    init(value: Int) {
        self.value = value
    }
}

var ss1 = SomeStruct(value: 10)
var ss2 = SomeStruct(value: 20)

ss1.value           // 10
ss2.value           // 20

ss1 = ss2
ss1.value           // 20
ss2.value           // 20

ss2.value = 999
ss1.value           // 20
ss2.value           // 999
앞서 본 클래스를 사용한 것과는 다른 양상이 나타난다.

만약 C++이나 Objective-C 혹은 C 등에 익숙했다면 이건 뭔가 좀 이상하다는 결론이 나온다. 왜냐하면 구조체도 실체화 될 때는 메모리에 인스턴스화 되어야 하기 때문이다. (물론 힙에 할당하지 않고 비포인터 타입의 구조체를 stack에 박아버리는 건 예외로 치자)

그런데 스위프트의 구조체는 이런 개념을 다르게 만들었다. 일반적으로는 구조체 형식이라도 레퍼런스로 동작되어야 한다고 생각하기 쉽지만 스위프트에서만은 값을 가질 수 있는 타입을 정의하기에 좋게 만들어 놓은 것이다.

구조체에게 있어서 '=' 연산자의 동작은 레퍼런스 복사가 아니라 값(value)의 복사로 동작하게끔 정의되어 있다.

즉, 스위프트(Swift)의 구조체(struct)는 값 타입(Value Type)이다.

다시금 정리해 보는 클래스와 구조체의 차이점

스위프트의 클래스(class)와 구조체(struct)와 문법적으로 매우 비슷한 구조를 가지고 있다. 한 컨테이너 안에 멤버 프로퍼티(Property)와 멤버 메소드(Method)를 가질 수 있다.

하지만 구조체는 상속(derive)이 불가능하고 그래서 오버라이드(override)도 불가능하다.

그리고 구조체는 프로퍼티를 변경하는 메소드에 반드시 mutating 이라는 지시어를 붙여서 상수 인스턴스로 선언될 때의 값의 변경을 일으키는 동작을 제어 할 수 있다.

마지막으로, 스위프트의 구조체는 레퍼런스(reference)가 아닌 값(value)을 가지는 개념으로 설계되어 있다.

이상으로 볼 때, 스위프트의 구조체(struct)는 데이터 타입(Data Type)을 정의하기 위한 용도로 볼 수 있다.

[관련글] Value and Reference Types - Swift Blog - Apple Developer
[관련글] Swift - let(상수선언)에 대해 파고들기
[관련글] Swift - 언제 class 대신 struct 를 사용하는가
[관련글] 스위프트(Swift) 가이드

댓글

이 블로그의 인기 게시물

소수점 제거 함수 삼총사 ceil(), floor(), round()

버전(Version)을 제대로 이해하기