Swift에서 클래스, 구조체에 대한 개념을 깊게 생각하지 않으면 그냥 인스턴스를 만들어내는 애.. 정도로 생각할 수 있다. 하지만, 메모리 구조적으로 보면 엄청난 차이가 존재하고 이전에 왜 안되지? 했던 부분들도 이해될 수 있기 때문에 정리해 본다.
# 🥨 Class와 Struct의 공통점
차이점을 알기 위해서는 뭐든 먼저 공통점을 알아야겠죠?
클래스와 구조체는 다양한 형식의 데이터를 한번에 관리하는 타입, 즉, 흔히 비유하는 것처럼 붕어빵 틀로 볼 수 있는 것은 아마 모두 아실 것 같아요! 아래처럼 타입속성, 인스턴스 속성, 인스턴스 메서드는 물론이고 계산 속성, 계산 타입속성, 서브스크립트 등등을 관리할 수 있습니다!
class someClass {
static let a = "a"
var b = "b"
func doSomething () {
print("doSomething")
}
}
struct someStruct {
static let c = "c"
var d = "d"
func doSomething () {
print("doSomething")
}
}
우리는 이 붕어빵 틀로 붕어빵을 찍어내어 메모리상에 올릴 것이고 이것을 인스턴스(instance)라고 부르죠! 다만 Swift에서는 class로 찍어낸 인스턴스를 객체(Object)라고 부르는 특징이 있구요.
또 다른 공통점에는 class와 struct 모두 extension을 사용하여 기능을 확장할 수 있고, Protocol을 채택할 수도 있다는 점입니다.
# 🥨 메모리 저장방식의 차이점
클래스와 구조체는 많은 차이점이 있는데 그 중 근본적이고 가장 큰 차이점 중 하나는 찍어낸 인스턴스를 메모리에 어떻게 저장시키는가 일 것입니다!
우선 간략하게 말하자면 구조체는 인스턴스 데이터가 값 형식으로 스택Stack영역에 저장되고! 클래스는 인스턴스 데이터가 참조형식으로 힙Heap영역에 저장되는 것입니다. 클래스 인스턴스는 힙 Heap에 메모리 공간을 탐색하고 저장하는 과정인 동적할당이 이루어지기 때문에 구조체보다 느리고 무겁다고 여겨지기도 하죠.
# 🥨 인스턴스 복사 시
자 그럼 값형식, 참조형식으로 저장된게 뭐가 그렇게 다른가!에 대한건 인스턴스를 복사해서 내부 속성 값을 바꿨을 때 기존 인스턴스의 속성값과 복사된 인스턴스의 속성값이 각각 어떤값을 가지냐를 보고 정확히 알 수 있죠 :)
아래와 같이 struct, class로 만든 인스턴스를 각각 복사해본다고 가정하겠습니다. 그럼 기존 인스턴스 struct1, class1과 인스턴스 struct2, class2가 생기겠죠?
struct SomeStruct {
var a = 1
var b = 2
}
var struct1 = SomeStruct() //스택에 인스턴스가 존재하고 내부 속성들이 "값"형태로 저장되어 있다
var struct2 = struct1 //스택에 struct1과 동일한 새로운 인스턴스가 또하나 생성되고 마찬가지로 내부 속성들이 "값"형태로 저장되어 있다
class SomeClass {
var c = 3
var d = 4
}
var class1 = SomeClass() //힙영역에 인스턴스가 존재하고 class1 변수는 힙에 생성된 인스턴스의 메모리 주소를 가리키고 있음
var class2 = class1 //class1의 메모리 주소를 전달한다
그리고 나서 기존 인스턴스인 struct1의 프로퍼티 a와 class1의 프로퍼티 c의 값을 각각 수정해준다고 했을 때 struct2의 프로퍼티 a와 class2의 프로퍼티 c의 값은 struct1와 class1의 변경이 어떻게 적용될까요?
//struct1, struct2가 각각 다른 인스턴스를 가지고 있기때문에 struct1.a로 속성에 접근해서 값을 바꾸어주어도
//struct2.a는 값이 바뀌지 않은 것을 확인할 수 있다.
struct1.a = 10
print(struct1.a) //10
print(struct2.a) //1
//class1, class2 모두 동일한 인스턴스를 가리키고 있기 때문에 class1.c로 속성 접근해서 값을 바꾸어주어도
//class2.c 에서 확인할 수 있듯이 동일하게 값이 바뀐것을 확인할 수 있다
class1.c = 100
print(class1.c) //100
print(class2.c) //100
- 우선 구조체는 복사했을 때 다른 메모리 공간에 "값"을 전달하기 때문에 struct1의 변경사항이 struct2에 전혀 영향을 주지 않죠.
- 반면 클래스는 복사했을 때 인스턴스가 저장된 "메모리 주소"를 전달하기 때문에 기존 인스턴스의 프로퍼티와 복사된 인스턴스의 프로퍼티가 같은 메모리 주소를 가리키고 있죠! 이 때문에 class1의 속성을 변경했다고 해도 class2에서 영향을 받는 것입니다.
# 🥨 메모리에서 제거하는 방법
그리고 클래스와 구조체의 인스턴스는 메모리에서 제거하는 방법도 각각 다릅니다!
struct의 인스턴스는 스택프레임 종료 시(구조체가 사용되고 있는 함수가 종료되었을 때) 메모리에서 자동으로 제거되는 반면, class는
힙영역에 저장된 메모리 (클래스의 인스턴스)는 자동으로 제거되지 않기 때문에 개발자가 직접 메모리에서 해제해 주는 등 작업을 해주어야합니다. 다만 swift는 ARC 시스템을 통해 힙Heap에 할당된 메모리가 더 이상 쓸모없어지면 자동으로 해제를 해주긴 합니다!
여기서 나오는 ARC 시스템은 매우 중요한 개념이기 때문에 나중에 정리하는 글을 써보려 합니다:)
# 🥨 Class, Struct 각각 언제 사용
후.. 그렇다면 이런 차이점을 가진 클래스와 구조체를 각각 언제 쓰면 좋을까요..!
구조체
- 메모리에 오래 저장하지 않아도 될 때 (사용되는 함수 영역에 종료되면 자동으로 메모리에서 해제)
- 상속 없이 가볍게 사용할 때 (class와 다르게 상속이 가능하지 않음)
클래스
- 메모리에 오랫동안 저장(관리)해야 할 때
- 상속이 필요할 때
가벼운 struct를 사용하면 되는 거 아니야?라고 하기엔 상속이 필요하거나하는 등의 다양한 경우에 class를 써야 하는 상황이 분명히 존재하기 때문에 역시 단편적으로 생각하면 안 되죠 ㅎㅎ 프로젝트를 하다 보면 점점 어느 상황에 각각 써야 하는지 감이 잡히는 것 같아요.
'Swift' 카테고리의 다른 글
[Swift] 메모리 관리 (1) | ARC와 메모리 누수 (feat.강한순환참조) (1) | 2023.10.16 |
---|---|
[Swift] Initialization (2) | 상속, 확장에서의 생성자 (0) | 2023.10.10 |
[Swift] Initialization (1) | 생성자의 종류 : 지정생성자, 편의생성자, 필수생성자, 실패가능생성자 (0) | 2023.10.04 |
[Swift] switch-case 문에서의 where절과 unknown 키워드 (0) | 2023.09.27 |
[Swift] 배열에서 index 사용 | enumerated, indices (0) | 2023.09.17 |