Struct의 특징
- Class와 다르게 상속이 불가능
- value type 입니다, 여기서 value type이란 Int, Double , String 등이 포함
- value type 이란 인스턴스를 전달할 때 값을 복사해 새로 만들지 참조하지는 않는다는 것을 의미
- Struct는 함수 호출이 끝나면 자동으로 메모리에서 해제되므로, 메모리 관리가 쉽고 효율적입니다
- Struct는 Class에 비해 컴파일러가 훨씬 더 빠르게 컴파일할 수 있습니다(참조같은 복잡한 일 대신 새롭게 만들기 때문에)
- Thread Safe 타입입니다
Struct를 사용하는 경우
- 다른 객체 또는 함수 등으로 전달될 때 - 참조가 아닌 복사를 원할 때, 원본 데이터가 변경되지 않음을 보장하고 싶을 때
- 자신을 상속할 필요가 없거나 자신이 다른 타입을 상속받을 필요가 없을 때
- 적은 메모리 사용을 원할 때 - struct은 value type이므로 스택을 사용해 간단한 데이터를 저장할 때 class 보다 메모리 효율이 좋습니다
struct 코드 정의
struct을 다음과 같이 정의해줍니다.
struct MyStruct {
let title: String
}
그리고 위에 struct을 가지고 인스턴스 2개를 만들었습니다.
private func structTest1() {
let objectA = MyStruct(title: "Starting title")
print("ObjectA: ", objectA.title)
let objectB = objectA
print("ObjectB: ", objectB.title)
}
하지만 이때 objectB 인스턴스의 title을 바꾸려고 시도하면 struct의 title을 let 에서 var로 바꿔야 한다는 에러 발생합니다.
변경 후
var objectB = objectA
struct의 프로퍼티 변경 시, class와 다른점
이어서 objectB 인스턴스의 title를 바꾸기 위해서 objectB를 let에서 var로 바꿔 에러를 해결하였지만 MyStruct의 title 또한 에러가 발생하게 됩니다.
objectB는 objectA의 값을 복사하여 값을 할당했는데 이 title 값을 수정하기 위해서는 title이 let이면 수정을 하지 못하기 때문입니다.
그렇기 때문에 title을 var로 변경하면 objectB의 title의 값을 변경할 수 있게 됩니다.
struct의 값을 바꾸게 되면 objectB 내부의 title 값을 바꾼다고 생각할 수 있지만 실은 새로운 구조체를 새로 정의하는 것입니다.
objectB.title = "Second title"
다음은 MyStruct의 title 프로퍼티를 var로 바꾼 이후 코드입니다.
struct MyStruct {
var title: String
}
결과를 확인해보면 ObjectB의 title 값을 수정하였지만 ObjectA에는 영향을 미치지 않는 것을 확인할 수 있습니다.
objectA의 값이 변하지 않은 이유는 구조체는 참조 타입이 아닌 value 타입이기 때문입니다.
objectB = objectA 는 인스턴스를 저장하는 것이 아닌 MyStruct(title: "Starting title")에 포함된 데이터를 objectB에 넣는 것이라고 생각하면 됩니다.
class 코드 정의
class을 다음과 같이 정의해줍니다.
class MyClass {
let title: String
init(title: String) {
self.title = title
}
}
그리고 위에 class을 가지고 인스턴스 2개를 만들었습니다.
private func classTest1() {
let objectA = MyClass(title: "Starting title!")
print("ObjectA: ", objectA.title)
let objectB = objectA
print("ObjectB: ", objectA.title)
}
class의 프로퍼티 변경 시, struct와 다른점
위에서 struct는 새롭게 인스턴스를 만든 이후 프로퍼티의 값을 바꾸는 것은 인스턴스를 let에서 var로 바꿔야 했습니다. struct는 인스턴스 자체를 할당하는 것이 아니라 완전히 새로운 struct를 만들어 내는 것이기 때문에 값을 참조하지 못하지만 class는 똑같은 인스턴스를 할당하여 해당 인스턴스의 값을 바꾸기 때문에 인스턴스가 let에서 var로 변경되지 않아도 가능합니다.
objectA는 메모리에 MyClass(title: "Starting title!") 값을 저장하고 objectB는 메모리에 저장된 objectA 값을 참조하게 되어 objectB의 값을 바꾸는 것은 메모리에 저장된 objectA의 값을 바꾸는 것과 같다.
private func classTest1() {
let objectA = MyClass(title: "Starting title!")
print("ObjectA: ", objectA.title)
let objectB = objectA
print("ObjectB: ", objectA.title)
objectB.title = "Second title"
print("ObjectB title changed.")
print("ObjectA: ", objectA.title)
print("ObjectB: ", objectB.title)
}
만약 위에 코드가 실행된다면 objectA 에는 MyClass 클래스 인스턴스를 의미하고 objectB는 objectA 인스턴스를 참조하고 있는 인스턴스가 됩니다. 즉 MyClass(title: "Strting title!") == objectA == objectB 관계가 이루어집니다.
Class와 Struct의 차이점 정리
class는 새로운 인스턴스를 만들 시 MyClass(title: "Strting title!") == objectA == objectB 관계가 이루어집니다.
let objectA = MyClass(title: "Starting title!")
let objectB = objectA
struct는 새로운 인스턴스를 만들 시 MyClass(title: "Strting title!") == objectA != objectB 관계가 이루어집니다.
objectB에는 objectA 참조값이 아닌 objectA와 똑같은 새로운 struct가 저장되기 때문
let objectA = MyStruct(title: "Starting title")
var objectB = objectA
모든 데이터 타입은 값 타입(value type) 또는 참조 타입(reference type)을 가지고 있습니다.
- 값 타입(Value type) : 각각의 고유의 메모리를 소유하며. struct, enum, array, tuples 들이 해당 타입에 속합니다.
- 참조 타입(Reference type) : 생성된 인스턴스들은 주소값을 공유하며. class가 해당 타입에 속합니다.
더 쉽게 이해할 수 있는 이미지를 가져왔습니다, 왼쪽은 class, 오른쪽은 struct 을 비유한 것입니다.
class는 복사된 커피컵에 커피가 채워지면 원본 커피컵 또한 커피가 채워지는데
struct은 복사된 커피컵에 커피가 채워지지만 원본 커피컵에는 커피가 채워지지 않습니다.
위에서 다룬 내용을 이해했다면 다음에 나오는 내용들도 이해할 수 있을 것입니다.
Struct 에서 프로퍼티 값 바꾸기
struct에서 프로퍼티 값을 바꿀 수 있는 여러가지 방법이 존재합니다.
방법1
예를들어 다음과 같은 구조체가 존재합니다.
struct MyStruct {
let title: String
}
이때 title을 var로 바꾸지 않고 let으로 새로운 인스턴스를 만드는 방법을 생각해봅시다. A라는 인스턴스를 만들고 A를 받는 B라는 인스턴스를 만들었다면 위에 내용을 돌이켜보면 값만 참조하게 됩니다.
var struct2 = CustomStruct(title: "Title1")
print("Struct2: ", struct2.title)
struct2 = CustomStruct(title: "Title2")
print("Struct2: ", struct2.title)
struct2 = CustomStruct(title: "New Title")
이때 다음과 같은 방법이 존재합니다
struct2의 title 값을 변경하고 싶으면 어차피 struct2 에 특정 title값을 넣는다면 새로운 CustomStruct(title: "새로운값") 의 구조체를 만들어 넣는 것과 같은 것인 것을 생각할 수 있게 됩니다.
struct2 = CustomStruct(title: "New Title")
이렇게 메서드를 만들어 사용하는 방법도 있겠습니다.
struct CustomStruct {
let title: String
func updateTitle(newTitle: String) -> CustomStruct {
CustomStruct(title: newTitle)
}
}
struct2 = struct2.updateTitle(newTitle: "New Title")
struct3 = struct2.updateTitle(newTitle: "Title3")
방법2 ( Mutating )
프로퍼티의 값을 바꾸는 방법으로는 메서드를 mutating으로 선언하는 방법이 있습니다.
mutating이란 '변화시키다' 라는 의미를 담고 있는 단어로 struct를 바꿀 수 있다는 것을 의미합니다.
struct MutatingStruct {
var title: String
mutating func updateTitle(newTitle: String) {
title = newTitle
}
}
이는 똑같이 새로운 프로퍼티를 나타내는 새로운 struct을 생성하는 것은 같지만 따로 반환받지 않아도 값이 바뀐다는 특징이 있습니다.
var struct4 = MutatingStruct(title: "Title1")
print("Struct4: ", struct4.title)
struct4.updateTitle(newTitle: "Title2")
print("Struct4: ", struct4.title)
굳이 mutating을 사용해야 하는가?
아래 코드는 title를 mutating(수정) 할 수 있습니다
struct MyStruct {
var title: String
}
그럼 결국 mutating 메서드를 만들어 title의 값을 바꾸기보다 title의 값을 직접 바꾸는 것이 더 심플하고 편리하지 않나? 라는 생각이 들 수 있습니다.
struct4.title = "Title2" // mutating 사용 X
struct4.updateTitle(newTitle: "Title2") // mutating 사용
물론 가능합니다, 하지만 프로퍼티의 값을 직접 바꾸는 것을 권장하지 않는 상황들이 존재할 수 있습니다, 그렇기 때문에 아래 코드처럼 private 으로 외부에서 직접 수정을 막는 코드를 작성할 수 있습니다.
struct MutatingStruct {
private var title: String
init(title: String) {
self.title = title
}
mutating func updateTitle(newTitle: String) {
title = newTitle
}
}
'SwiftUI' 카테고리의 다른 글
Value Type, Reference Type, Stack, Heap, Struct, Class 사용되는 상황들, @StateObject 가 클래스여야 하는 이유 (0) | 2024.05.20 |
---|---|
Class, ARC, Weak Self (0) | 2024.05.20 |
Subscripts (0) | 2024.05.17 |
async let 으로 비동기 작업들을 동시에 수행하기 (1) (0) | 2024.05.17 |
Chat GPT API 사용하기 (3) | 2024.05.15 |