🐦 Swift

[Swift] 구조체(Struct), 클래스(Class)

dev_zoe 2023. 6. 8. 11:20
반응형

본 포스팅은 '스위프트 프로그래밍 (3판)' 도서앨런 Swift 문법 마스터스쿨 강의를 참고하여

Swift 프로그래밍에 대해 정리하는 글입니다.

혹시 틀린 부분이 있거나 질문이 있으시다면 언제든지 댓글 달아주시면 정말 감사하겠습니다 :)


구조체와 클래스

- 프로그래머가 데이터를 용도에 맞게 묶어 표현하고자 할 때 유용

- 클래스는 틀이고 이 틀을 통해 찍어낸 붕어빵을 객체, 인스턴스 라고 함 (객체는 클래스에서만 해당하는 이름)

- 둘은 거의 완전히 동일하나, 값 타입이냐 참조 타입이냐에 가장 큰 차이를 두고 있음 (값 타입과 참조 타입은 아래에서 다룸)

 

구조체

 1) 구조체 정의

struct 구조체 이름 {
	프로퍼티와 메서드
}

 

2) 구조체 인스턴스 생성 및 초기화

struct Developer {
    var name: String
    var techStack: String
    
}
 
var yuri: Developer = Developer(name: "유리", techStack: "iOS")
yuri.name = "조이"
yuri.techStack = "Swift" //마침표(.)를 통해 프로퍼티 접근 및 변경 가능 (var일 때만!)
 
print(yuri)  //Developer(name: "유리", techStack: "iOS")

 

클래스

1) 클래스 정의

class 구조체 이름: 부모 클래스 {
	프로퍼티와 메서드
}

- 구조체와는 달리 상속이 가능하다. 는 점이 가장 큰 차이점

 

2) 클래스 인스턴스 생성 및 초기화

- 구조체와는 달리, 클래스의 인스턴스는 참조 타입이므로 let 으로 선언하여도 프로퍼티 값을 변경할 수 있음

class Developer {
    var name: String = "yuri"
    var techStack: String = "iOS"
}
 
let yuri: Developer = Developer() //초기값을 지정해주지 않아도 됨
yuri.name = "조이"
yuri.techStack = "Swift" //구조체와는 달리 let임에도 프로퍼티 값을 변경할 수 있음
 
print(yuri)  //Developer(name: "조이", techStack: "Swift")

 

초기화와 생성자 (이니셜라이저)

인스턴스를 생성한다 = 즉 속성의 값을 초기화하여 실체화된 데이터를 만든다 라는 의미 이므로 반드시 속성의 초기화가 필요

속성의 초기화를 돕는 메소드가 init 이라는 메소드고, 이니셜라이저라고 부름

이니셜라이저는 오버로딩을 지원하기 때문에, 다양한 파라미터로 인스턴스를 생성할 수 있도록 지원함

class Person {
    var name: String // 타입만 지정하여 생성  // 붕어빵에 들어가는 재료만 정해둠
    var weight: Int // 저장속성
    
    // 생성자
    init(name: String, weight: Int) {  // 붕어빵 재료 정해서 만들기!
        self.name = name    // self: 자기 자신을 가리킴 -> 둘다 name이면 어떤걸 가리키는지 혼동되므로 더 구체적으로 구분하기 위해 사용
        self.weight = weight
    }
}

 

속성을 옵셔널 타입으로 지정하면, 해당 값을 필수적으로 초기화하지 않아도 되며 이 때는 nil로 초기화됨

class Person {
    var name: String?
    var weight: Int
    
    // 생성자
    init(weight: Int) {  // 붕어빵 재료 정해서 만들기! -> name은 생략 가능
        // self.name = name    // self: 자기 자신을 가리킴 -> 둘다 name이면 어떤걸 가리키는지 혼동되므로 더 구체적으로 구분하기 위해 사용
        self.weight = weight
    }
}

 

구조체와 클래스의 차이점 (알게된 내용 계속 추가 예정)  ⭐️  ⭐️  ⭐️  ⭐️  ⭐️ 

1) class는 참조 타입, struct는 값 타입

 

💡 참조 타입?

힙 영역에 인스턴스가 만들어지고, 인스턴스가 할당된 변수에는 해당 인스턴스를 가리키는 주소값이 저장되어 있음

따라서 클래스는 인스턴스를 복사하면, 값을 복사하는 것이 아니라 인스턴스 주소를 복사하는 것이므로 인스턴스의 값을 변경하면 같이 변경된다.

class Person {
    var name = "사람"
}


var p = Person()    // Person의 메모리 주소를 가리킴
p.name

var p2 = p       // (클래스)     // p와 p2 모두 같은 메모리 주소를 가리키는 것임


p.name = "혜리"

p.name
p2.name // 둘이 똑같은 메모리 공간을 가리키는 것이기 때문에 동일한 결과가 나옴

 

- 또한 변수에는 인스턴스 데이터를 가리키는 주소가 들어있으므로, let으로 인스턴스를 생성한다고 하더라도 참조 타입 내부의 값 변경이 가능하다는 점을 유의해야함 (즉 let으로 선언하면 주소값이 바뀌지 않는다는 의미이지 인스턴스 내부의 값이 바뀌지 않는다는 의미가 아님)

class PersonClass {
    var name = "사람"
    var age = 0
}

let pclass = PersonClass()

pclass.name = "사람1" // let 임에도 변경 가능 -> pclass의 가리키는 메모리 주소가 변경할 수 없다는 의미이므로
pclass.name

 

💡 값 타입?

변수에는 해당 인스턴스 데이터가 모두 저장되어있음.

따라서 값 타입은 인스턴스를 복사하면, 해당 인스턴스의 복사본을 만들어서 스택 영역에 할당함

struct Animal {
    var name = "동물"
}

var a = Animal()
a.name

var a2 = a     // a와 a2는 서로 다른 인스턴스임
a.name = "강아지"

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

 

- 참조 타입과 달리, 주소를 가리키는 것이 아닌 별개의 인스턴스를 가리키므로 let으로 선언하면 해당 인스턴스의 값 변경이 불가능함

struct AnimalStruct {
    var name = "동물"
    var age = 0
}

let astruct = AnimalStruct()

astruct.name = "동물1" // 변경 불가 -> astruct의 데이터를 변경할 수 없다는 의미

- Int, String, Dictionary ... 등과 같은 스의프트의 모든 기본 데이터 타입은 구조체이며 값타입이다.

-> 인스턴스를 복사하면 값 자체가 복사되어 새로 생성

 

2) class는 상속이 가능하며, struct는 상속이 불가능하다.

3) class는 멤버 와이즈 생성자가 없고 대신에 편의 생성자와 필수 생성자가 존재하며, struct는 멤버 와이즈 생성자가 존재한다.

4) class에는 소멸자가 있지만 struct는 없다.

 

구조체와 클래스는 각각 언제 사용하는가?

https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes

 

Choosing Between Structures and Classes | Apple Developer Documentation

Decide how to store data and model behavior.

developer.apple.com

 

1) 애플 공식문서에 따르면, 반드시 class를 사용할 때가 아니라면 struct를 디폴트로 사용하라고 권장하고 있다.

- 어차피 class와 struct가 상속이 가능한가 아닌가 를 제외하고는 기능이 거의 동일하기 때문에, 값타입인 구조체가 관리하기에는 더 용이하기에 struct를 쓰라고 하는것 같다.

 

2) 따라서 상속의 가능성이 있는 경우나 serialize를 통해 파일로 저장할 경우가 발생하는 경우가 아니고선 struct를 사용해도 무방하다.

 

반응형