열거형
대부분 언어에서 열거형은 요소라 불리는 이름이 있는 값으로 구성된 데이터 타입인 것에 비해 스위프트는 더 강력한 기능들을 제공한다. 스위프트 열거형은 기능 면에서 클래스와 구조체에 매우 가까우면서도 여전히 다른 언어에서 사용하는 열거형과 같은 방식으로 사용할 수 있다.
Devices 열거형을 예로 살펴보자.
enum Devices {
case iPod
case iPhone
case iPad
}
스위프트으 열거형이 다른 언어의 열거형과 다른 점 중 하나는 원시 값raw value으로 불리는 값을 할당할 수 있다는 점이다.
enum Devices: String {
case iPod = "iPod"
case iPhone = "iPhone"
case iPad = "iPad"
}
이렇게 할당한 값은 rawValue 프로퍼티를 사용해 가져올 수 있다.
let device = Devices.iPhone.rawValue // "iPhone"
케이스 값 옆에 연관 값associated values을 저장할 수도 있다. 연관 값은 어떠한 타입도 될 수 있으며, 각 케이스에 따라 달라질 수 있다. 이 점은 케이스 타입에 커스텀한 정보를 추가로 저장할 수 있게 해준다.
enum Devices {
case iPod(model: Int, year: Int, memory: Int)
case iPhone(model: String, memory: Int)
case iPad(model: String, memory: Int)
}
이렇게 정의한 열거형 값은 다음과 같이 사용할 수 있다.
let iPhoneModel = Devices.iPhone(model: "6", memory: 64)
let iPadModel = Devices.iPad(model: "Pro", memory: 256)
연관 값을 사용하고자 하면 다음과 같이 할 수 있다.
switch iPhoneModel {
case .iPhone(let model, let memory):
print("iPhone \(model) \(memory)") // iPhone 6 64
default:
print("Can't be Reached")
}
스위프트 열거형은 클래스나 구조체처럼 연산 프로퍼티나 생성자 또는 메서드를 가질 수도 있다.
enum RandomCode: String, CaseIterable {
case a, b, c, d, e, f
static func randomCase() -> RandomCode {
let randomValue = Int.random(in: 0..<RandomCode.allCases.count)
return RandomCode.allCases[randomValue]
}
static func randomCase2() -> RandomCode? {
RandomCode.allCases.randomElement()
}
}
// RandomCode의 케이스 중 random한 값이 출력됨
print(RandomCode.randomCase())
print(RandomCode.randomCase())
print(RandomCode.randomCase())
if let randomCode = RandomCode.randomCase2() {
print(randomCode)
}
열거형은 연관 값을 메서드나 프로퍼티와 결합해 더 강력하게 사용할 수 있다.
enum BookFormat {
case PaperBack (pageCount: Int, price: Double)
case HardCover (pageCount: Int, price: Double)
case PDF (pageCount: Int, price: Double)
case EPub (pageCount: Int, price: Double)
case Kindle (pageCount: Int, price: Double)
}
위 열거형은 연관 값 검색이 매우 복잡하다. 검색 시 마다 매번 switch 구문을 작성해야하기 때문이다.
값을 검색하는 전역 함수를 만들 수도 있겠지만, 스위프트에서는 연산 프로퍼티computed property를 열거형에 추가해 활용할 수도 있다.
enum BookFormat {
case PaperBack (pageCount: Int, price: Double)
case HardCover (pageCount: Int, price: Double)
case PDF (pageCount: Int, price: Double)
case EPub (pageCount: Int, price: Double)
case Kindle (pageCount: Int, price: Double)
var pageCount: Int {
switch self {
case .PaperBack(let pageCount, _):
pageCount
case .HardCover(let pageCount, _):
pageCount
case .PDF(let pageCount, _):
pageCount
case .EPub(let pageCount, _):
pageCount
case .Kindle(let pageCount, _):
pageCount
}
}
var price: Double {
switch self {
case .PaperBack(_, let price):
price
case .HardCover(_, let price):
price
case .PDF(_, let price):
price
case .EPub(_, let price):
price
case .Kindle(_, let price):
price
}
}
}
var myBook = BookFormat.EPub(pageCount: 30, price: 10000)
print(myBook.pageCount) // 30
print(myBook.price) // 10000.0
이러한 연산 프로퍼티는 switch 구문의 복잡성을 숨기면서 사용할 때에는 손쉬은 닷dot 문법 인터페이스를 제공한다.
또한 열거형에 메서드를 추가할 수도 있다. 아래와 같이 여러 종류의 책을 살 경우 20% 할인받는다고 가정해보자.
... enum BookFormat
func purchaseTogether(otherForamt: BookFormat) -> Double {
(self.price + otherForamt.price) * 0.8
}
...
var myBook = BookFormat.EPub(pageCount: 30, price: 10000)
var myAnotherBook = BookFormat.PaperBack(pageCount: 50, price: 6000)
myBook.purchaseTogether(otherForamt: myAnotherBook) // 12800
위 예시들을 보듯 스위프트의 열거형은 다른 언어의 열거형보다 훨씬 강력하다. 그러나 열거형의 남용은 피해야한다. 열거형이 클래스나 구조체를 대체할 순 없다. 열거형의 내부는 여전히 이름 있는 값을 갖는 유한한 셋finite set으로 구성된 데이터 타입이다.