값 타입과 참조 타입

값 타입(Value Type) 참조 타입(Reference Type)
구조체, 열거형, 튜플 클래스, 클로저

값 타입

  • 구조체, 열거형, 튜플 타입 자체는 데이터 메모리 영역 에서 생성된다
  • 스택 메모리 영역 에 인스턴스가 생성된다
  • 인스턴스 복사시)
    • 새로운 값이 할당되어 스택 메모리 영역에 쌓인다
    • 그러므로 복사본의 내부 프로퍼티를 변경하면 그 복사본의 값이 변하고 원본 값은 변하지 않는다
  • 인스턴스 let으로 선언시)
    • 인스턴스의 내부 프로퍼티들이 var로 선언되었더라도 그 값을 변경할 수 없게된다

참조 타입

  • 클래스나 클로저 타입 자체도 값 타입과 마찬가지로 데이터 메모리 영역 에서 생성된다
  • 힙 메모리 영역 에 생성된 인스턴스의 데이터를 저장한다
  • 스택 메모리 영역 에서는 힙 메모리 영역 에 있는 인스턴스의 데이터의 메모리 주소를 저장한다
  • 인스턴스 복사시)
    • 스택 메모리 영역에 새로운 데이터가 생겨서 원본 데이터와 같은 메모리 주소를 가리킨다
    • 그러므로 복사본의 내부 프로퍼티를 변경하면 가리키고 있는 메모리 주소의 데이터가 변경된다 (부수 효과 발생)
  • 인스턴스 let으로 선언시)
    • 인스턴스를 let으로 선언하면 그 인스턴스가 가르키고 있는 메모리 주소를 변경할 수 없게 된다

swift에서는 값 타입이 훨씬 더 강력한 기능을 한다

Stack의 장단점

  • Stack은 Push, Pop을 통해 메모리를 할당/해제한다. → 스택 포인터만 움직이면 되므로 한번의 명령으로 메모리 할당과 해제 수행
  • 선형적인 데이터 구조다. 단순하고 효율적이다.

하지만 컴파일 타임에 스택에 필요한 메모리 크기를 미리 알 수 있어야 한다. 하나의 스택이 올라갔을 때 그 크기를 확정할 수 있어야, 다음 스택이 생길 때 할당할 메모리를 알 수 있다.

또 스택이 끝나면 해제되어 버린다. 데이터를 스택에 할당하면 다른 객체에서 데이터에 접근하기가 어렵다.

Heap의 장단점

  • Heap은 임의의 메모리 주소에 메모리를 할당/해제한다.
  • 동적할당에 사용된다
  • 메모리 할당을 할 때 순차적인 탐색이 필요하다.
  • 더 이상 사용하지 않는 데이터를 탐지하기 위해 참조 카운팅(Reference counting)을 해야 한다.
  • Stack보다 할당/해제에 드는 오버헤드가 크다.

다만 Heap은 런타임에 메모리 할당 크기가 변할 수 있고, 여러 객체나 스코프에서 참조값을 통해 데이터에 접근이 가능하다.

우리는 주로 값 타입은 Stack을 쓰고, 참조 타입은 Heap을 쓴다고 배운다. 그렇지 않은 경우도 있다. 즉, 시멘틱의 관점에서는 ‘불변성’을 가지면서도, 실제 메모리는 힙을 사용할 수도 있다는 뜻이다.

Struct vs Class 언제 무엇을 이용할까

  1. 최대한 구조체 사용
    • 값 타입은 부수 효과가 없고 또 다른 메모리의 주소를 참조하고 있지 않으므로 특정 부분의 코드만 보고도 이해가 가능하고, 결과를 예측할 수 있다
    • 멀티 스레드 환경에서 공유 데이터를 가지지 않아 안전하고 간단하다
    • 스택 사용으로 인해 메모리 자원의 할당 및 해제의 성능이 효율적이다!
    • 구조체도 프로토콜과 값 타입의 조합을 통해 다형성을 만들어 낼 수 있으므로 상속이 필요 없다! (swift는 Protocol-oriented)
  2. Cocoa framework의 타입 계층, 및 obj-c 런타임이 필요할 경우 클래스 사용

    • UIKit를 서브클래싱할 경우 class 사용.
  3. 고유성을 가진 데이터를 제어할 경우→ 클래스사용
    • 데이터의 변화를 감지하기 위해서는 데이터의 고유성이 필요하다 ( 그 데이터의 식별할 수 있는 고유한 형태가 있어야 변경됨을 감지할 수 있다)
    • 또한 앱의 여러 곳에서 사용할 때 한 영역에서 적용한 수정이 다른 영역에도 적용되기를 원하면 클래스를 사용한다
    • 주로 파일 관리나 네트워크 연결과 같은 작업을 다룰 때 클래스를 사용한다
  4. 많은 양의 복사→ 구조체 내에 클래스 사용
    • 값 타입(구조체)내에 데이터가 굉장히 많거나 복사가 많이 일어날 경우, 내부에 클래스를 활용한다 (구조체 내에 참조 타입으로 별도의 저장소 생성)
    • 복사를 최소화하여 성능을 최적화한다
    • 구조체가 여러번 복사되고 할당되더라도 참조 타입 내의 데이터는 값이 복사되지 않고 참조만 늘어나기 때문에 효율적으로 사용 가능하다!
    • Copy-on-Write를 사용하여 변경이 일어날 경우에만 복사해준다
    • 하지만 구조체 내에 참조 타입도 가급적 숫자를 줄이면 좋다 !

참고

https://velog.io/@eddy_song/value-reference-decision

https://icksw.tistory.com/257

https://velog.io/@eddy_song/value-reference-decision#간단-설명-값-타입과-참조-타입의-차이