-
Notifications
You must be signed in to change notification settings - Fork 2
Description
열거형(enumeration)은 관련된 값으로 이루어진 그룹을 공통의 타입으로 선언해 타입 안전성(type-safety)을 보장하는 방법
C나 Objective-C가 Integer 값들로 열거형을 구성한 것에 반해,
Swift에서는 케이스 값이 String, Character, Integer, 부동 소수점 타입들도 사용 가능
열거형은 1급 클래스 타입(first-class types), 참조타입이 아닌 값 타입으로 분류 됨 - 따라서 상속 불가
열거형은 전통적으로 클래스에 의해서만 지원되는 많은 기능을 함께 포함
현재 값에 대한 추가적인 정보를 제공하는 계산된 프로퍼티(computed properties),
또는 열거형을 대표하는 값과 연관된 기능성을 제공하는 인스턴스 메소드를 지원
또한 초기화를 지정하거나, 초기 선언을 확장해 사용 가능
열거형 문법
enum 키워드를 선언하고 코드 블록 안에 전체적인 정의를 중괄호 내부 바디에 작성
enum SomeEnumeration {
// enumeration definition goes here
}열거형에 정의된 값들이 열거 케이스(enumeration case)
새로운 열거 케이스를 추가하기 위해 case 키워드를 사용
Swift의 열거형은 C나 Objective-C와 달리 정수를 기본 값으로 갖지는 않는다
예제에서 north, south, east, west는 각각 암시적으로 0, 1, 2, 3값을 갖지 않는다
대신 Swift에서 열거형의 각 case는 CompassPoint 타입으로 선언된 온전한 값으로 구성
각각의 열거형은 새로운 타입을 정의
타입의 이름은 Swift의 다른 타입처럼 대문자로 시작
열거형 타입은 복수형보다는 단수형으로 작성
enum CompassPoint {
case north
case south
case east
case west
}
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
// 여러 케이스를 한 줄로 표현할 수 있다
var directionToHead = CompassPoint.west
// directionToHead의 타입은 CompassPoint의 값 중 하나로 초기화될 때 추론
directionToHead = .east
// 한번 directionToHead가 CompassPoint로 선언됐다면 CompassPoint의 다른 값을 축약해서 설정 가능Switch 구문에서 열거 값 매칭하기
switch 구문으로 열거형의 개별 값을 매칭할 수 있다
switch문은 반드시 열거형의 모든 경우(cases)를 완전히 포함해야 한다
만약 위에서 case .west가 생략되었다면 코드는 컴파일 되지 않음
만약 열거형의 모든 case의 처리를 기술하는게 적당하지 않다면
default case로 처리되지 않는 case에 대한 로직을 구성할 수 있다
var directionToHead = CompassPoint.south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// Prints "Watch out for penguins"
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// Prints "Mostly harmless"열거 케이스 순회하기
열거형의 모든 케이스가 있는 컬렉션을 구성해야 하는 경우
ex. 메뉴 / 카테고리 케이스에 대한 전체적인 컬렉션
CaseIterable 프로토콜을 열거형이 준수하도록 하여
Swift는 모든 케이스의 컬렉션을 allCases 프로퍼티를 사용해 접근할 수 있다
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"
for beverage in Beverage.allCases {
print(beverage)
}
// coffee
// tea
// juice1) 연관 값 (Associated Value)
열거형의 케이스에 상수나 변수를 설정하고 나중에 값을 확인 가능
하지만, 케이스 값과 함께 다른 타입의 값을 저장하는 게 유용한 경우도 발생
이러한 추가적인 정보를 연관 값(associated value)이라 하며, 케이스를 사용할 때 마다 달라짐
Swift의 열거형은 어떤 타입의 관련 값이든 저장하도록 구성 가능
해당 값의 타입은 열거형이 필요할 때마다 달라질 수 있음
- 1줄의 UPC 포맷 바코드가 부여된 물건
- 각각의 바코드는 5자리 제조사 코드 번호와 5자리 제품 번호를 갖고 있음
- 스캔이 정확하게 됐는지 확인하기 위한 체크 숫자도 포함
- QR코드라 불리는 2D 바코드가 부여된 물건
- ISO 8859-1 캐릭터를 사용하고 2,953개의 문자로 구성
// UPC 바코드는 정수 네 개의 튜플로 저장하는 게 유용
// QR코드는 임의의 문자열로 저장
// 해당 선언은 정의는 실제 Int값이나 String값을 제공하지는 않는다
// Barcide.upc거나 Barcode.qrCode와 같을 때 Barcode 상수와 변수가 저장할 수 있는 관련 값의 타입을 정의
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
// 새로운 UPC 바코드 생성
// 새로운 변수인 productBarcode에 연관 튜플의 값인 (8, 85909, 51226, 3)을 갖는 Barcode.upc를 할당
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
// 같은 제품 변수에 Barcode 타입인 QR 코드도 할당 가능
// 기존 Barcode.upc와 튜플의 정수 값은 새로운 Barcode.qrCode와 문자열 값으로 대체
// Barcode 타입의 상수 / 변수는 .upc와 .qrCode 중 하나의 case로 저장 가능
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")서로 다른 바코드 타입을 switch문을 이용하여 확인 가능
하지만, 이때 연관 값은 switch문의 일부분으로써 추출
각각의 연관 값을 상수 / 변수로 추출하고 switch문의 블록 안에서 사용 가능
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
// 만약 열거 케이스의 모든 연관 값이 상수나 변수로 추출된다면 가독성을 위해,
// 하나의 let 또는 var 키워드를 케이스 앞에 작성 가능Raw Values
연관 값의 대안으로써, 열거 케이스는 raw values로 불리는 기본값으로 미리 채워질 수 있다
해당 기본값들은 모두 같은 타입으로 한정됨
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}raw values는 String, Character, Integer, 부동 소수점 타입으로 할당 가능
각각의 값은 열거형 선언 안에서 고유한 값으로 지정해야 함 - 중복 불가
raw values는 연관 값과 같지 않음
raw values는 코드에서 열거형을 처음 선언할 때 미리 채워지기에 특정한 열거 케이스의 raw values는 항상 같은 값
연관 값은 새로운 상수나 변수를 만들 때마다 새로 정해진다
1) 암시적으로 할당된 Raw Values
정수나 문자열 raw values를 저장하는 열거형을 사용할 때, 각 케이스의 raw values를 명시적으로 할당할 필요가 없다.
ex. raw values에 정수를 사용할 경우
명시적으로 할당할 경우, 나머지 케이스의 암시적 값은 이전 케이스의 값보다 하나 크다
만약 첫 번째 케이스가 값을 갖고 있지 않다면 값은 0이 기본 값
ex. raw values에 문자열을 사용할 경우
각 케이스 이름이 암시적 값으로 정해짐
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
// 예시에서 Planet.mercury는 명시적 raw value인 1을 할당
// 두번째 케이스인 Planet.venus는 암시적 raw value인 2를 갖게 된다
enum CompassPoint: String {
case north, south, east, west
}
// case 선언에서 주어진 이름을 바탕으로 암시적으로 할당
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"2) Raw Value로 초기화 하기
만약 raw value 타입과 함께 열거형을 초기화 한다면,
열거형은 raw value 값의 타입(rawValue라 불리는 매개 변수)을 취하는 initializer를 자동적으로 받게 된다
raw value initializer는 모든 rawValue 기반의 케이스를 찾을 수는 없으며 실패할 가능성이 있다
따라서, 항상 열거 케이스 또는 nil이 될 수 있는 옵셔널 열거 케이스를 반환
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}
// Prints "There isn't a planet at position 11"재귀적 열거형
재귀적 열거형(recursive enumeration)은 하나 이상의 열거 케이스의 연관 값으로 열거의 다른 인스턴스를 포함하는 열거
열거형 케이스가 재귀적임을 나타내려면 앞에 indirect키워드를 붙여 표시
즉, 특정한 케이스의 연관 값의 멤버 중에 자기 자신을 넣는 것이 가능
컴파일러는 필요한 간접 참조 계층을 삽입하도록 지시
// 연관 값을 갖는 모든 열거 케이스에 indirect를 표시하고 싶으면 열거형의 시작 부분에 붙일 수 있다
// addition과 multiplication 케이스 역시 ArithmeticExpression을 연관 값으로 가지는데 해당 연관 값은 중첩된 표현을 가능케 한다
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
// 다음 코드는 ArithmeticExpression 재귀적 열거형으로 (5 + 4) * 2를 만드는 것을 보여 준다
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
// Prints "18"
