본문 바로가기
iOS/Swift

클래스&구조체 메모리 관점에서의 차이

by 나리._. 2021. 12. 17.

이번 포스팅에서는 클래스와 구조체의 가장 큰 차이 2가지

1. 메모리 저장 방식

2. 상속 가능 여부

중 메모리 저장 방식 차이에 대해 정리할 것이다!

 

 

(아래 코드들은 playground에서 실행했기 때문에, main 함수 안이라고 가정한 뒤 공부한 것입니다.

즉, 전역 변수가 아닌 main 함수의 스택 영역이라고 생각하며 코드를 작성한 것입니다.)

클래스

예시 1.

class Dog {
    var name = "강아지"
    var weight = 0
    
    func sit() {
        print("\(self.name)가 앉았습니다.")
    }
    
    func layDown() {
        print("누웠습니다.")
    }
}

// 객체 생성
var bori = Dog()
var choco = Dog()

// 객체의 속성에 접근
choco.name = "초코"

bori, choco (변수 자체)는 스택 영역에 생성.

실제 데이터는 힙 영역에 생성.

(힙 영역에는 해당 데이터의 클래스(틀)의 주소가 담겨있다.)

 

bori, choco(변수 in 스택)에는 실제 메모리 주소(실제 데이터 in 힙)만 담겨있음.

 

 

예시 2.

class Person {
    var name = "사람"
}

var p = Person()    // 메모리 주소: x1234
p.name	// 사람

var p2 = p       // (클래스) // 메모리 주소: x1234

p.name = "혜리"

p2.name // p2의 이름 또한 혜리

p2.name = "수지"

p.name
p2.name // p의 이름 또한 수지

(복사 시) 값을 전달하는 것이 아니고, 저장된 주소를 전달하기 때문에 p, p2 모두 동일한 데이터를 가리킴

 

 

 

구조체

예시 1.

struct Bird {
    var name = "새"
    var weight = 0.0
    
    func fly() {
        print("날아갑니다.")
    }
}

func doSomething(){
	// 인스턴스 생성
    var aBird = Bird()

	aBird.name = "참새1"

    aBird.fly()

    var bBird = Bird()    
}

doSomething()

인스턴스 데이터를 모두 스택(Stack)에 저장.

위와 같이 실행을 하면 스택 영역에 함수 생성!

함수가 실행되는 동안에만 aBird bBird 만들어졌다가 없어짐.

 

 

예시 2.

struct Animal {
    var name = "동물"
}

var a = Animal()
a.name // 동물

var a2 = a       // (구조체)
a.name = "강아지"

a.name // 강아지
a2.name // 동물

(복사 시) 값을 전달할 때마다 복사본을 생성!

즉, 다른 메모리 공간을 생성하기 때문에 a, a2 각각 다른 데이터

 

위의 저장 방식을 살펴보았을 때,

구조체 인스턴스는 스택에 저장되기 때문에 클래스 인스턴스보다 가볍고 쓰기 편하다는 느낌을 받을 수 있다.

 

 

 

클래스와 구조체의 let과 var키워드

// 클래스
class PersonClass {
    var name = "사람"
    var age = 0
}

// 구조체
struct AnimalStruct {
    var name = "동물"
    var age = 0
}

// (클래스, 구조체) 인스턴스 상수로 선언
let pclass = PersonClass()
let astruct = AnimalStruct()


// 상수인데 변경가능? o
// 왜?
pclass.name = "사람1"
pclass.name


// 상수인데 변경가능? x
// 클래스는 되는데 얜 왜 안돼?
//astruct.name = "동물1" // 에러!!!!
astruct.name

 

클래스 인스턴스를 상수(let)로 선언하면,

스택에 메모리 주소만 저장이 되기 때문에 메모리 주소가 let으로 저장되는 것.
즉, 새로운 객체를 가리키는 메모리 주소로 변경이 불가.

하지만 해당 메모리의 속성들은 각 속성이 let인지 var인지에 따라 결정.

따라서 위의 코드에서는 속성이 변수(var)로 선언되었기 때문에 변경 가능!!

 

여기서 구조체는 왜 속성 값이 변경이 안될까?

 

이것 또한 위의 데이터 저장 방식과 같은 맥락이다.
구조체의 경우, 메모리 구조가 스택에 생성되며 let으로 선언 시, 가지고 있는 속성 모두가 let이 된다!

 

 

당연하게만 생각했던,

스위프트의 기본타입, 컬렉션 타입, 튜플, 열거형..등을 상수로 선언하면 변경할 수 없는 것.

이것에는 이유가 있었다...

모두 값 형식(구조체)으로 구현되어 있기 때문!

 

스위프트에서 참조형식으로 구현된 것은 클래스와 클로저!

 

 

정리를 해서 살펴보면 !

 

1) 클래스

    - 참조 형식(Reference Type)

    - 인스턴스 데이터는 힙(Heap)에 저장

    - 저장된 힙을 가리키는 변수는 스택에 저장하고, 메모리 주소 값이 힙(Heap)을 가리킴

    - (복사 시) 값을 전달하는 것이 아니고, 저장된 주소를 전달

    - 힙(Heap)의 공간에 저장 -> ARC시스템을 통해 메모리 관리(주의해야 함)

 

2) 구조체

    - 값 형식(Value Type)

    - 인스턴스 데이터를 모두 스택(Stack)에 저장

    - (복사 시) 값을 전달할 때마다 복사본을 생성 (다른 메모리 공간 생성)

    - 스택(Stack)의 공간에 저장 -> 스택 프레임 종료 시, 메모리에서 자동 제거

 

다음과 같다 !