Search

[Swift 공식 문서] 8. Enumerations

Enumerations

Enumeration(열거형) 은 연관성이 있는 값들을 모아놓은 타입을 말합니다.
Swift 의 열거형은 퍼스트 클래스 객체입니다. 클래스에서 지원하는 여러 기능들을 사용할 수 있습니다.
1.
computed property
2.
instance method
3.
initializer
4.
extension
5.
protocol
참고로 열거형은 reference type 이 아닌 value type 입니다.
따라서 복사 수행시, 원본과 값은 같으나, 본질은 다른 객체가 생성됩니다.

Enumeration Syntax

Swift 에서 enum 은 타입이므로 대문자 카멜케이스를 사용하여 이름을 정의합니다.
enum 키워드와 {} 을 이용해 열거형을 정의할 수 있습니다.
enum SomeEnumeration { // enumeration definition goes here }
Swift
복사
아래는 compass(나침반) 네가지 주 방향에 대한 예시입니다.
enum CompassPoint { case north case south case east case west }
Swift
복사
열거형에 정의된 값들은 열거형의 case 들입니다. case 키워드를 이용해 새 열거형 case 들을 사용할 수 있습니다.
Swift 의 열거형 case 는 C, Objective C 와 다르게 정수형 값의 집합을 초기값으로 갖지 않습니다. 대신, 서로다른 열거형 case 들은 그자체로 값(CompassPoint 타입의)입니다.
쉼표로 구분해 여러개의 case 를 한줄에 나타낼 수 있습니다.
enum Planet { case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune }
Swift
복사
각각의 열거형 정의는 새로운 타입을 정의합니다.. Swift 의 다른 타입들처럼 그들의 이름은 대문자로 시작합니다. 열거형 타입을 복수형이 아닌 단수형 이름을 부여하세요.
var directionToHead = CompassPoint.west
Swift
복사
directionToHead 의 타입은 CompassPoint 의 가능한 값중 하나로 초기화될 때, 추론됩니다. 일단 directionToHead 가 CompassPoint 로 정의되면, 다른 CompassPoing 값을 더 축약된 형태로 할당할 수 있습니다.
directionToHead = .east
Swift
복사
directionToHead 의 타입이 이미 정해졌기 때문에 새값을 할당할 때 타입의 이름을 생략할 수 있습니다. 이는 코드의 가독성을 높여줍니다.

Matching Enumeration Values with a Switch Statement

case 는 그 자체가 고유의 값입니다.
각각의 enumeration 값들을 switch 문에 대응 시킬 수 있습니다.
directionToHead = .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"
Swift
복사
switch 문은 항상 완전해야합니다. 발생가능한 모든 case 를 다루어야 합니다. 예를 들어 위 예시에서 case .north: 를 생략한다면 컴파일 에러가 발생합니다. 모든 case 를 다루기 힘들다면 default 키워드를 사용해서 switch 문을 완성하세요.
let somePlanet = Planet.earth switch somePlanet { case .earth: print("Mostly harmless") default: print("Not a safe place for humans") } // Prints "Mostly harmless"
Swift
복사

Iterating over Enumeration Cases

몇몇 열거형에 대해서는 열거형의 case 전부에 대한 collection 을 제공받는 것이 매우 유용합니다. 열거형의 이름 뒤에 : CaseIterable 를 써 넣으면 됩니다.
Swift 는 열거형 타입의 allcases 프로퍼티를 사용해 모든 케이스에 대한 컬렉션을 사용이 가능해집니다.
enum Beverage: CaseIterable { case coffee, tea, juice } let numberOfChoices = Beverage.allCases.count print("\(numberOfChoices) beverages available") // Prints "3 beverages available"
Swift
복사
위 예시에는 열거형의 이름 옆에 CaseIterable 을 작성해 줌으로써 이 열거형의 모든 case 를 아루르는 collection 을 allCases 프로퍼티를 통해 접근하는 것을 볼 수 있습니다.
for-in looop 에서 순회또한 가능합니다.
for beverage in Beverage.allCases { print(beverage) } // coffee // tea // juice
Swift
복사

Associated Values

앞 섹션에서 우리는 열거형의 case들이 그 자체로 정의된 값이 되는 지를 보았습니다. Planet.earth 에를 상수 또는 변수로 설정이 가능하고, 이러한 값을 나중에 체크할 수 있습니다. 그러나 가끔은 case 값들에 따라 다른 타입의 값들을 저장하는 것이 유용할 때가 있습니다. 이러한 추가적인 정보를 associated value 연관값이라고 합니다.
열거형에 모든 타입의 연관값을 저장할 수 있으며, 이 타입은 서로 달라도 됩니다.
예를들어 두가지 타른 타입의 바코드인 UPC 형태의 1D 바코드, QR 코드를 예시로 들겠습니다.
UPC 형태의 바코드는 0~9 사이의 숫자를 사용합니다.총 5자리의 4개의 정수를 나타낼 수 있으며, 하나는 제조사 코드 넘버, 하나는 제품 코드 넘버를 나타냅니다. 나머지 2개의 정수는 앞선 코드들이 올바르게 스캔되었는지를 확인하는 체크 비트입니다.
QR 코드는 2953 길이의 문자열을 인코딩 할 수 있습니다.
위 두개의 바코드 타입에 대한 열거형은 아래와 같이 정의할 수 있습니다.
enum Barcode { case upc(Int, Int, Int, Int) case qrCode(String) }
Swift
복사
위 코드는 아래와 같은 의미를 지닙니다
Barcode 라는 이름의 열거형 타입을 정의하며, 이는 upc 값과 (Int, Int, Int, Int) 타입의 연관값을 가진다. 또한 qrCode 의 값은 String 타입의 연관값을 가진다
이러한 정의는 어떠한 실질적인 정수 또는 문자열 값을 제공하지 않습니다. 단지 연관값의 타입만을 정의할 뿐입니다.
새로운 바코드를 생성해봅시다.
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
Swift
복사
productBarcode 라는 변수를 만들어 이에 ((8, 85909, 51226, 3)) 을 연관 값으로 가지는 Barcode.upc 값을 할당하였습니다.
같은 제품을을 다른 타입의 바코드에 할당해 봅시다.
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
Swift
복사
이 시점에서 원본 Barcode.upc 와 그 정수 값은 새로이 Barcode.qrCode 와 그 문자열 값으로 대체됩니다. Barcode 타입의 상수 또는 변수는 .upc.qrCode 모두를 저장할 수 있습니다. 하지만 한번에 하나씩만 저장이 가능합니다.
switch 문을 사용해 다른 바코드 타입을 체크할 수 있습니다. Switch 문을 활용해 연관값을 추출해봅시다. 연관값을 추출해 상수 또는 변수로 선언 해 Switch 문의 case body 내에서 사용이 가능합니다.
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."
Swift
복사
만약 열거형 case 대한 모든 연관값이 상수로 추출되거나 모두 변수로 추출된다면 하나의 var 또는 let 을 case 이름앞에 작성해주면 됩니다.
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."
Swift
복사
enum 열거형을 사용하면 서로 다른 타입을 저장하는 변수를 생성할 수 있을 뿐더러 이에 대해 switch 문으로 다른 타입에 대한 다른 로직의 수행을 구현할 수 있다.!!

Raw Values

위 예시에서 우리는 열거형의 case 들이 어떻게 서로 다른 타입의 연관값들을 저장할 수 있는지를 보았습니다. 연관 값에 대한 대안으로 열거형의 case 들은 초기값 (raw value) 로 미리 선점될 수 잇습니다. 이는 모두 같은 타입입니다.
아래는 ASCII 값들을 저장하는 열거형 case 에 대한 예시입니다.
enum ASCIIControlCharacter: Character { case tab = "\t" case lineFeed = "\n" case carriageReturn = "\r" }
Swift
복사
case 별로 각각 다른 값의 raw value 를 가져야합니다
Hashable 프로토콜을 따르는 모든 타입이 raw value 의 타입으로 지정될 수 있습니다.
위 코드에서 Character 타입으로 정의된 열거형의 raw value 를 볼 수 있습니다. 이들은 흔히 사용되는 ASCII 제어 문자로 정의 되었습니다.
Raw value 들은 string character integer floating point number 등등의 타입으로 정의가 가능합니다. 각각의 raw value 들은 열거형의 선언 내부에서 유일해야합니다
Raw value 는 associated value 와는 전혀 다름에 주의해야 합니다. Raw value 는 열거형 코드내에서 미리 선점되는 값인 반면에, raw value 는 새로운 상수 또는 변수를 하나의 열거형 case 에 대해 생성할 때 비로소 설정됩니다. 그리고 몇번이고 반복해서 재 설정이 가능합니다.

Implicitly Assigned Raw Values

암묵적으로 할당된 raw value
정수형 또는 문자열 타입의 raw value 를 저장하는 열거형을 다룰 때,각각의 case 들에 raw value를 미리할당할 필요는 없습니다. 만약 할당하지 않았다면 Swift 는 자동으로 값을 할당합니다.
예를들어, 정수형 타입의 raw value 는 분명하게 초기값을 할당하지 않는다면 해당 case 의 앞 case의 raw value 보다 는 1 만큼 크도록 자동 할당됩니다.
enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune }
Swift
복사
위 예시에서
venus 는 2, mars 는 3, jupyter 는 4 ,,, 이런식으로 암묵적 할당됩니다.
문자열들에 raw value 들이 사용되지 않았다면 각각의 case 들의 값들은 암묵적으로 해당 case 의 이름으로 설정 됩니다.
enum CompassPoint: String { case north, south, east, west }
Swift
복사
위 예시에서 CompassPoint.south 는 암묵적으로 "south" 라는 raw value 를 가집니다.
각각의 case 에 대해 rawValue 프로퍼티를 사용해 raw value 에 접근할 수 있습니다.
let earthsOrder = Planet.earth.rawValue // earthsOrder is 3 let sunsetDirection = CompassPoint.west.rawValue // sunsetDirection is "west"
Swift
복사

Initializing from a Raw Value

Raw value 로 초기화 하기
raw value 의 값을 인자로 받는 initialize 를 사용하면 이 raw value 에 해당하는 열거형 case 를 정의할 수 잇습니다. 이 initializer 는 열거형 case 또는 nil 값을 반환합니다. 이를 이용해 열거형의 객체를 생성할 수 있습니다.
let possiblePlanet = Planet(rawValue: 7) // possiblePlanet is of type Planet? and equals Planet.uranus
Swift
복사
해당 raw value 와 일치하는 case 가 있다면 해당 case 를 리턴하며, 없다면 nil 값을 리턴합니다. 위 코드에서 이를 할당한 possiblePlanetPlanet? 타입으로 선언되었으며, 이의 값은 Planet.uranus 와 같습니다.
raw value 를 인자로 받는 열거형 initializer 는 항상 옵셔널 타입을 반환함에 주의하자.
만약 11 번 위치의 행성을 찾고자 한다면, raw value initializer 가 리턴한 optional Planet 값은 nil 일 것이다. 이는 해당 raw value 를 가지는 case 가 존재하지 않기 때문이다.
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"
Swift
복사

Recursive Enumerations

recursive enumeration(재귀 열거형)이란 다른 열거형의 객체를 연관값으로 가지는 열거형을 이르는 말입니다. indirect 키워드를 활용해 열거형의 case 를 recursive 로서 사용이 가능합니다. indirect 키워드는 컴파일러에게 indirection 에 필요한 layer 를 삽입하도록 지시합니다.
아래는 간단한 산술식을 저장하는 열거형에 대한 예시입니다.
enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) }
Swift
복사
indirect 키워드를 열거형의 선언 맨 앞에 작성해서 모든 열거형의 case 들이 연관 값으로 indirection이 가능하도록 설정할 수 있습니다.
indirect enum ArithmeticExpression { case number(Int) case addition(ArithmeticExpression, ArithmeticExpression) case multiplication(ArithmeticExpression, ArithmeticExpression) }
Swift
복사
위 열거형은 세가지 종류의 산술식을 저장할 수 있습니다.
1.
number : 순수 숫자
2.
addition : 두 표현식의 덧셈
3.
multiplication : 두 표현식의 곱셈
(5 + 4) * 2 와 같은 표현식을 예로 들자면 왼쪽의 괄호로 둘러쌓은 덧셈과, 오른 편의 곱셈으로 이루어져있습니다. 이러한 데이터는 중첩되어있으며, 열거형은 이러한 종류의 중첩을 표현하기위해 지원이 필요합니다. 바로 recursive 입니다.
(5 + 4) * 2 를 recursive 열거형을 통해 생성해 봅시다.
let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
Swift
복사
열거형 타입으로 선언된 five, four 이 다시 열거형 ArithmeticExpression.addition 의 인자로 넘겨져 새로운 열거형 객체인 sum 을 생성하는 모습입니다.
이렇게 생성된 열거형 객체 sum 은 또다시 ArithmeticExpression.multiplication 의 인자로 넘겨져 새로운 열거형 객체인 product 를 생성합니다.
이를 활용해 실제 재귀 함수에서 사용할 수도 있습니다.
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"
Swift
복사