Swift

Swift - Struct와 Class 선택과 차이점

Goniii 2024. 12. 6. 19:28

Struct와 Class: Swift에서의 선택과 차이점

Swift를 공부하다 보면 Struct와 Class를 어떻게, 언제 사용해야 할지 고민되는 순간이 옵니다. 이 글에서는 두 타입의 공통점과 차이점, 그리고 상황에 따라 어떤 타입을 선택해야 할지에 대해 알아보겠습니다.

 

공통점

1. Struct와 Class 모두 속성(프로퍼티)과 메서드 정의 가능

struct PersonStruct {
    var name: String  // 속성
    func greet() {    // 메서드
        print("Hello, \\(name)!")
    }
}

class PersonClass {
    var name: String = "soo" // 속성
    func greet() {           // 메서드
        print("Hello, \\(name)!")
    }
}

 

2. 프로토콜 준수 가능

protocol Person {
    var name : String { get }
    func greet()
}

struct PersonStruct : Person{
    var name: String  // 속성
    func greet() {    // 메서드
        print("Hello, \\(name)!")
    }
}

class PersonClass : Person{
    var name: String = "soo" // 속성
    func greet() {           // 메서드
        print("Hello, \\(name)!")
    }
}

 

3. 확장 (extension) 사용 가능

extension PersonStruct {
    func getName() -> String{
        return name
    }
}

extension PersonClass {
    func getName() -> String{
        return name
    }
}

 

4. 초기화 메서드(init) 사용 가능

  • Struct는 초기화 메서드가 정의되지 않으면 자동으로 기본 생성자를 생성해줌
struct PersonStruct {
    var name: String
    func greet() {
        print("Hello, \(name)!")
    }
}

class PersonClass {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func greet() {
        print("Hello, \(name)!")
    }
}

 

 

차이점

특징 Struct Class
메모리 할당 값 타입(Value Type) 참조 타입(Reference Type)
메모리 저장 위치 스택(Stack) 힙(Heap)
복사 방식 복사 시 독립적인 복사본 생성 (값 복사) 복사 시 동일한 참조 공유
상속 가능 여부 상속 불가 상속 가능
deinit 지원 여부 deinit 불가 소멸자(deinit) 지원
Mutating 키워드 mutating 키워드를 사용해 메서드에서 속성 변경 가능 메서드 내에서 속성을 자유롭게 변경 가능
Identity 각 인스턴스가 독립적이고 고유한 값 참조가 동일한 객체는 동일한 Identity를 가짐

 

1. 상속

  • Struct는 상속이 불가능 하지만, Class는 상속 가능
class ParentClass {
    func parent(){
        print("parent")
    }
}

class PersonClass : ParentClass {
    var name: String
    init(name: String) {
        self.name = name
    }
    func greet() {
        print("Hello, \\(name)!")
    }
}

struct ParentStruct{
    func parent(){
        print("parent")
    }
}

struct PersonStruct : ParentStruct{ // Error: Inheritance from non-protocol type 'ParentStruct'
    var name: String
    func greet() {
        print("Hello, \\(name)!")
    }
}

 

 

 

2. 값 타입 VS 참조 타입

  • Struct는 값 타입이므로 변수나 상수에 할당될 때, 복사가 이루어지고 각각 독립적으로 존재
var struct1 = PersonStruct(name: "Alice")
var struct2 = struct1 // var struct2 = PersonStruct(name: "Alice")와 의미가 같음
struct2.name = "Bob"
print(struct1.name) // "Alice"
print(struct2.name) // "Bob"

/* 
 Ex)
struct1의 메모리 주소 : 0x123123
struct2의 메모리 주소 : 0x321321
*/

→ Struct는 값 타입이기 때문에 메모리에 각각의 객체가 할당됨

 

 

  • Class는 참조 타입이므로 변수나 상수에 할당될 때 동일한 객체를 참조
var class1 = PersonClass(name: "Alice") // 객체의 메모리 주소를 할당
var class2 = class1  // class1에 할당된 메모리 주소 값을 받기 때문에 같은 객체를 가리킴
class2.name = "Bob"
print(class1.name) // "Bob"
print(class2.name) // "Bob"

/* 
 Ex)
class1의 메모리 주소 : 0x123123
class2의 메모리 주소 : 0x123123
*/

→ Class는 참조 타입이므로 변수에 할당될 때, 객체의 메모리 주소를 할당함

 

 

👉 메모리 구조

→ Person 인스턴스는 힙에 생성되며, 힙 주소가 스택에 저장됨

  • 힙에 저장된 데이터를 가리키는 참조는 스택에 저장됨

 

힙과 스택의 차이 (Struct와 Class와의 관계 포함)

특징 스택(Stack) 힙(Heap)
저장 위치 Struct와 같은 값 타입 Class와 같은 참조 타입 객체
메모리 할당 방식 정적(Static) 할당 (빠름) 동적(Dynamic) 할당 (느림)
자체 크기 결정 컴파일 타임에 크기 결정 런 타임에 크기 결정
데이터 생명 주기 자동으로 관리 (스코프를 벗어나면 해제) ARC 또는 수동 관리
복사 동작 값 자체를 복사 (독립적 복사본 생성) 참조만 복사 (같은 객체를 참조)
속도 빠름 비교적 느림
용량 작음
사용 목적 독립적인 값 관리가 필요한 경우 공유나 상속이 필요한 객체 관리

 

 

 

언제 Struct를 사용하고 Class를 사용해야 할까?

Struct 사용:

  • Struct 사용 권장
  • 데이터가 간단하고 크기가 작을 때.
  • 복사가 자주 일어나도 독립성을 유지해야 할 때.
  • 값의 불변성을 보장하고자 할 때.

Class 사용:

  • 참조를 공유하거나, 데이터의 상태를 여러 객체가 공동으로 관리할 때.
  • 상속을 통해 계층 구조를 설계해야 할 때.
  • 데이터가 더 복잡하거나 크기가 큰 경우.