Swift에서 struct는 굉장히 자주 쓰이는데, 클래스랑 비슷해 보이지만 사실 꽤 다르다.
딱 두 단어로 정리하면 값 타입이고, 상속 불가이다.
그래서 오늘은 Swift의 구조체에 대해 차근차근 정리해보려 한다.
struct Person {
let name: String
let age: Int
}
let foo = Person(name: "Foo", age: 20)
Swift의 구조체는 내부에 저장된 속성들을 기준으로 자동으로 생성자(init)를 만들어준다.
이런 걸 멤버와이즈 이니셜라이저라고 부른다.
즉, 따로 init 안 만들어도 이렇게 Person(name:age:)처럼 바로 만들 수 있다는 얘기다.
struct CommodoreComputer {
let name: String
let manufacturer: String
init(name: String) {
self.name = name
self.manufacturer = "Commodore"
}
}
여기서 궁금한 게 하나 생긴다.
"기본 생성자도 있는데 굳이 init을 왜 만들지?"
그 이유는 manufacturer처럼 내부에서 고정 값을 넣고 싶을 때!
외부에서 굳이 넣을 필요 없는 값을 내부에서 세팅해줄 수 있어서다.
struct CommodoreComputer2 {
let name: String
let manufacturer = "Commodore"
}
이렇게 속성에 기본값을 주면?
Swift는 알아서 생성자를 만들어준다.
그래서 CommodoreComputer2(name:)만 있어도 된다.
struct Person2 {
let firstName: String
let lastName: String
let fullName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
self.fullName = "\(firstName) \(lastName)"
}
}
이렇게 init에서 fullName을 지정해줄 수도 있지만,
사실 더 Swift스러운 방법은 따로 있다.
struct Person22 {
let firstName: String
let lastName: String
var fullName: String {
"\(firstName) \(lastName)"
}
}
mutatingstruct Car {
var currentSpeed: Int
mutating func drive(speed: Int) {
currentSpeed = speed
}
}
여기서 궁금한 점.
"왜 굳이 mutating이라는 키워드가 필요한 걸까?"
그 이유는 구조체는 값 타입이라 내부에서 값을 바꾸려면 스스로를 다시 바꿔야 하기 때문.
그래서 명시적으로 mutating을 붙여줘야 한다.
// currentSpeed가 let이면 당연히 안바뀜.
var car = Car(currentSpeed: 100)
car.drive(speed: 300)
let copy = car
car.drive(speed: 500)
print(copy.currentSpeed) // 300
이 예제를 보면 명확하다.
copy는 car를 복사한 값이라, 이후 car가 바뀌어도 copy는 그대로다.
struct LivingThing {
init() {
print("I'm living thing")
}
}
// 에러 발생: 구조체는 상속이 안 된다
// struct Animal: LivingThing { }
이쯤에서 이런 의문이 생긴다.
"왜 struct는 상속이 안 되는 거지?"
Swift는 컴포지션(구성)을 더 중요하게 본다.
클래스처럼 복잡하게 상속보단, 작은 구조체들을 조립해서 쓰는 걸 더 선호한다.
그래서 swiftUI 잠깐 배웠을 때, 데이터는 struct. 데이터를 제어하는건 class를 사용한다고 들었던거같다.
copy 메서드 패턴으로 새 인스턴스 만들기struct Bike {
let manufacturer: String
let currentSpeed: Int
func copy(currentSpeed: Int) -> Bike {
Bike(manufacturer: self.manufacturer, currentSpeed: currentSpeed)
}
}
값 타입에서 가장 많이 쓰이는 패턴 중 하나가 바로 이거다.
기존 값을 유지하면서 일부 속성만 바꾼 새로운 인스턴스를 만들고 싶을 때 유용하다.
let bike1 = Bike(manufacturer: "HD", currentSpeed: 20)
let bike2 = bike1.copy(currentSpeed: 30)
다른 방법도 있다.
struct Bike {
let manufacturer: String
let currentSpeed: int
mutating func moveToOrigin() {
// 이런식으로 작성하면 self 전체를 다른 값으로 통째로 바꾸는 것도 가능.
self = Point(manufacturer: "YD", currentSpeed: 30)
}
}
init 정의 가능mutating 키워드로 명시Swift의 struct는 값 기반 프로그래밍을 위한 핵심 도구다.
불변성, 복사, 컴포지션을 잘 이해하고 쓰면, Swift가 훨씬 더 명확하고 안전한 언어로 다가온다.