값 타입은 데이터를 전달할 때 그 값의 복사본을 생성하여 전달합니다. 즉, 원본과 복사본은 완전히 독립적인 개체가 됩니다.
Swift에서는 구조체(Struct), 열거형(Enum), 기본 데이터 타입(Int, String, Bool 등)이 값 타입입니다.
값 타입의 주요 특징은 데이터의 안전성이 높다는 점입니다.
데이터를 전달할 때마다 복사본을 사용하기 때문에 원본 데이터를 실수로 변경할 위험이 적습니다.
struct Point {
var x: Int
var y: Int
}
var originalPoint = Point(x: 10, y: 20)
var copiedPoint = originalPoint
copiedPoint.x = 30
originalPoint와 copiedPoint는 완전히 다른 독립적인 개체이기 때문에,
copiedPoint를 변경해도 originalPoint에 영향을 미치지 않습니다.
참조 타입(Reference Type)
참조 타입은 데이터를 전달할 때 값의 복사본을 전달하는 것이 아니라 데이터가 저장된 메모리 위치(참조)를 전달합니다.
클래스(Class)가 참조 타입의 대표적인 예입니다.
참조 타입의 경우, 두 변수가 같은 메모리 위치를 참조할 수 있으므로, 한 변수를 통한 데이터의 변경이 다른 변수에도 영향을 미칩니다.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
var originalPerson = Person(name: "홍길동")
var referencedPerson = originalPerson
referencedPerson.name = "임꺽정"
참조 타입의 특징은 인스턴스를 변수나 상수에 할당하거나 함수에 전달할 때,
실제 데이터의 복사본을 만드는 것이 아니라 해당 데이터를 가리키는 참조(메모리 주소)를 전달하기 때문에
referencedPerson을 변경하면 originalPerson에도 영향을 미칩니다.
따라서 referencedPerson.name을 "임꺽정"으로 변경하면, referencedPerson이 가리키는 메모리 위치에 있는 name 프로퍼티가 변경됩니다
값타입과 참조타입을 알아봤으니, 다시 본론으로 돌아가서
Struct와 Class의 차이점을 예시코드로 보여드릴게요
Struct
struct UserData {
var name: String
var age: Int
mutating func updateUser(_ name: String, _ age: Int) {
self.name = name
self.age = age
print("이름 : \(name), 나이 : \(age)")
}
}
var shark = UserData(name: "상어", age: 16)
print("이름 : \(shark.name), 나이 : \(shark.age)")
// 이름 : 상어, 나이 : 16
var hanabi = shark
hanabi.name = "하나비"
hanabi.age = 27
print("이름 : \(shark.name), 나이 : \(shark.age)")
// 이름 : 상어, 나이 : 16
print("이름 : \(hanabi.name), 나이 : \(hanabi.age)")
// 이름 : 하나비, 나이 : 27
shark.updateUser("천랑성", 20)
// 이름 : 천랑성, 나이 : 20
struct의 핵심은 값 복사입니다.
그래서 hanabi에 shark의 값을 복사하고 hanabi의 속성을 변경해도 shark의 속성은 변경되지 않습니다.
하지만,
값 복사이면서 copy-on-write이기 때문에 대입할 때 복사가 일어나는 것이 아닌,
수정이 발생할 때 값이 복사됩니다.
그렇기 때문에
hanabi.name = "하나비"
이 부분에서 실제로 복사가 일어나는 것을 볼 수 있습니다.
아 그리고, struct는 메서드 앞에 mutating을 꼭 해줘야 하는데요,
왜냐면 구조체는 값타입(value type)이기 때문에 메서드 안에서 프로퍼티 변경이 불가능하답니다.
그래서 mutating을 붙이면 해당 객체가 다시 생성되면서 변경이 가능하게 됩니다.
Class
class Phone {
var name: String
var color: String
// struct와 달리 class는 이니셜라이즈를 지정해야합니다.
init(name: String, color: String) {
self.name = name
self.color = color
}
func updatePhone(name: String, color: String) {
self.name = name
self.color = color
print("폰 : \(name), 색상 : \(color)")
}
}
let iPhone8 = Phone(name: "iPhone8", color: "red")
print("폰 : \(iPhone8.name), 색상 : \(iPhone8.color)")
// 폰 : iPhone8, 색상 : red
var iPhoneXs = iPhone8
iPhoneXs.name = "iPhoneXs"
iPhoneXs.color = "grey"
print("폰 : \(iPhone8.name), 색상 : \(iPhone8.color)")
// 폰 : iPhoneXs, 색상 : grey
print("폰 : \(iPhoneXs.name), 색상 : \(iPhoneXs.color)")
// 폰 : iPhoneXs, 색상 : grey
iPhoneXs.updatePhone(name: "iPhoneX", color: "black")
// 폰 : iPhoneX, 색상 : black
class는 reference type이기 때문에 iPhoneXs를 iPhone8을 대입하고 iPhoneXs의 속성을 변형하면 iPhone8에 대한 속성도 함께 변형이 된답니다.
뿐만 아니라, class는 메서드 안에서 프로퍼티 변경이 가능하기 때문에 mutating을 붙이지 않아도 됩니다
그래서 어떤 경우에 사용해야하는데?
구조체(Struct)를 사용하는 경우
독립성이 중요할 때
구조체의 인스턴스는 값 타입이므로, 복사될 때마다 새로운 인스턴스가 생성되니 공유 상태를 피하고 싶을 때 구조체를 사용합니다.
데이터 모델링이 간단할 때
작은 데이터 모델이나 간단한 값의 집합을 다룰 때 구조체를 사용하는 것이 좋습니다.
상속이 필요 없을 때
구조체는 상속을 지원하지 않기에, 상속이 필요 없는 경우에 구조체를 사용하는 것이 적합합니다.
클래스(Class)를 사용하는 경우
인스턴스 공유가 필요할 때
여러 변수나 함수가 동일한 인스턴스를 참조해야 할 필요가 있을 때 클래스를 사용합니다.
상속을 사용해야 할 때
기능 확장이나 코드 재사용성을 위해 상속을 사용하고 싶을 때 클래스를 사용합니다.
마치며
구조체와 클래스 사이의 선택은 구현하려는 기능, 성능 요구 사항, 메모리 관리 방식 등 여러 요소를 고려해야 합니다.
Swift는 구조체 사용을 권장하지만, 앱의 특정 요구 사항에 따라 클래스의 사용이 불가피할 수 있습니다.
편하게 읽으실려면 여기
Struct, Class의 공통점과 차이점을 알아봅시다
공통점
차이점
이러한 공통점과 차이점이 있습니다.
자 여기서 value type, reference type이 뭐냐?
값 타입(Value Type)
값 타입은 데이터를 전달할 때 그 값의 복사본을 생성하여 전달합니다. 즉, 원본과 복사본은 완전히 독립적인 개체가 됩니다.
Swift에서는 구조체(Struct), 열거형(Enum), 기본 데이터 타입(Int, String, Bool 등)이 값 타입입니다.
값 타입의 주요 특징은 데이터의 안전성이 높다는 점입니다.
데이터를 전달할 때마다 복사본을 사용하기 때문에 원본 데이터를 실수로 변경할 위험이 적습니다.
originalPoint와 copiedPoint는 완전히 다른 독립적인 개체이기 때문에,
copiedPoint를 변경해도 originalPoint에 영향을 미치지 않습니다.
참조 타입(Reference Type)
참조 타입은 데이터를 전달할 때 값의 복사본을 전달하는 것이 아니라 데이터가 저장된 메모리 위치(참조)를 전달합니다.
클래스(Class)가 참조 타입의 대표적인 예입니다.
참조 타입의 경우, 두 변수가 같은 메모리 위치를 참조할 수 있으므로, 한 변수를 통한 데이터의 변경이 다른 변수에도 영향을 미칩니다.
참조 타입의 특징은 인스턴스를 변수나 상수에 할당하거나 함수에 전달할 때,
실제 데이터의 복사본을 만드는 것이 아니라 해당 데이터를 가리키는 참조(메모리 주소)를 전달하기 때문에
referencedPerson을 변경하면 originalPerson에도 영향을 미칩니다.
따라서 referencedPerson.name을 "임꺽정"으로 변경하면, referencedPerson이 가리키는 메모리 위치에 있는 name 프로퍼티가 변경됩니다
값타입과 참조타입을 알아봤으니, 다시 본론으로 돌아가서
Struct
와Class
의 차이점을 예시코드로 보여드릴게요Struct
struct의 핵심은
값 복사
입니다.그래서 hanabi에 shark의 값을 복사하고 hanabi의 속성을 변경해도 shark의 속성은 변경되지 않습니다.
하지만,
값 복사이면서
copy-on-write
이기 때문에 대입할 때 복사가 일어나는 것이 아닌,수정이 발생할 때 값이 복사됩니다.
그렇기 때문에
이 부분에서 실제로 복사가 일어나는 것을 볼 수 있습니다.
아 그리고, struct는 메서드 앞에 mutating을 꼭 해줘야 하는데요,
왜냐면 구조체는 값타입(value type)이기 때문에 메서드 안에서 프로퍼티 변경이 불가능하답니다.
그래서 mutating을 붙이면 해당 객체가 다시 생성되면서 변경이 가능하게 됩니다.
Class
class는 reference type이기 때문에 iPhoneXs를 iPhone8을 대입하고 iPhoneXs의 속성을 변형하면 iPhone8에 대한 속성도 함께 변형이 된답니다.
뿐만 아니라, class는 메서드 안에서 프로퍼티 변경이 가능하기 때문에 mutating을 붙이지 않아도 됩니다
그래서 어떤 경우에 사용해야하는데?
구조체(Struct)를 사용하는 경우
구조체의 인스턴스는 값 타입이므로, 복사될 때마다 새로운 인스턴스가 생성되니 공유 상태를 피하고 싶을 때 구조체를 사용합니다.
작은 데이터 모델이나 간단한 값의 집합을 다룰 때 구조체를 사용하는 것이 좋습니다.
구조체는 상속을 지원하지 않기에, 상속이 필요 없는 경우에 구조체를 사용하는 것이 적합합니다.
클래스(Class)를 사용하는 경우
여러 변수나 함수가 동일한 인스턴스를 참조해야 할 필요가 있을 때 클래스를 사용합니다.
기능 확장이나 코드 재사용성을 위해 상속을 사용하고 싶을 때 클래스를 사용합니다.
마치며
구조체와 클래스 사이의 선택은 구현하려는 기능, 성능 요구 사항, 메모리 관리 방식 등 여러 요소를 고려해야 합니다.
Swift는 구조체 사용을 권장하지만, 앱의 특정 요구 사항에 따라 클래스의 사용이 불가피할 수 있습니다.
따라서, 상황에 따라 가장 적합한 타입을 선택하는 것이 중요하겠죠?