주니곰의 괴발노트
Swift - 열거형 본문
정의
- 관련된 값의 그룹을 위한 일반 타입을 정의하고 코드에서 Type-Safe 방법으로 값을 동작하게 함
- Swift에서의 열거형은 훨씬 유연하고 열거형의 각 케이스에 값을 제공하지 않아도 됨
- 값 (원시값) 이 각 열거형 케이스로 제공된다면 그 값은 문자열, 문자 또는 정수 또는 부동 소수 타입일 수 있음
- 열거형 케이스는 각 다른 케이스 값으로 저장될 모든 타입의 관련된 값을 지정할 수 있음
- 하나의 열거형의 일부로 관련된 케이스의 공통 세트를 정의할 수 있으며 각각은 연관된 적절한 타입의 다른값 세트를 가지고 있음
- Swift의 열거형은 그 자체로 1급 타입
- 열거형의 현재값에 대한 추가 정보를 제공하는 계산 프로퍼티와 열거형이 나타내는 값과 관련된 기능을 제공하는 인스턴스 메서드와 같이 전통적으로 클래스에서만 지원되는 많은 기능을 채택
- 열거형은 케이스 초기값을 제공하기 위해 초기화를 정의할 수도 있음
- 열거형은 기존 구현을 넘어 기능적으로 확장될 수도 있고 표준 기능을 제공하기 위해 프로토콜을 준수할 수 있음
열거형 구문
enum SomeEnumeration {
// enumeration definition goes here
}
- 열거형은 enum 키워드와 중괄호 안에 모든 정의를 위치시켜 나타냄
enum CompassPoint {
case north
case south
case east
case west
}
- 열거형 안에 정의된 값 (north, south, east, west)은 열거형 케이스 (enumeration cases)
- 새로운 열거형 케이스를 나타내기 위해 case 키워드를 사용
- Swift 열거형 케이스는 C와 Objective-C 처럼 기본적으로 정수값을 설정하지 않으므로 CompassPoint 에서 north, south, east, west 는 0, 1, 2, 3 과 같지 않음
- 열거형 케이스는 CompassPoint 의 명시적으로 정의된 타입으로 자체 값
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
- 여러개의 케이스는 콤마로 구분하여 한줄로 표기 가능
- 각 열거형 정의는 새로운 타입으로 정의
- Swift의 다른 타입처럼 대문자로 시작하는 그들의 이름 (CompassPoint 와 Planet)이 타입
var directionToHead = CompassPoint.west
- directionToHead 타입은 CompassPoint 의 가능한 값 중 하나로 초기화 될 때 유추됨
directionToHead = .east
- directionToHead 는 CompassPoint 로 선언되고 더 짧게 점 구문을 사용하여 다른 CompassPoint 값을 설정 가능
- directionToHead 의 타입은 이미 알고 있으므로 값을 설정할 때 타입을 삭제할 수 있음
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"
- switch 구문으로 각각의 열거형 값을 일치시킬 수 있음
- switch 구문은 열거형 케이스를 고려할 때 완벽해야 함
- .west 에 대한 case 가 생략된다면 CompassPoint 케이스에 대해 모든 리스트이 고려되지 않았으므로 이 코드는 컴파일 되지 않음
- 완전성을 요구하면 열거형 케이스가 실수로 생략되지 않도록 함
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// Prints "Mostly harmless"
- 모든 열거형 케이스에 대해 case 를 제공하는 것이 적절하지 않은 경우 명시적으로 해결되지 않은 사례를 포함하는 default 케이스를 제공할 수 있음
열거형 케이스 반복
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
// juice
- 일부 열거형의 경우 열거형의 모든 케이스를 수집하는 것이 유용
- 열거형 이름 뒤에 : CaseIterable 을 작성하여 활성화
- Swift는 열거형 타입에 allCases 프로퍼티로 모든 케이스를 수집하고 방출
- Beverage 열거형에 모든 케이스를 포함하는 콜렉션에 접근하기 위해 Beverage.allCases 를 작성
- 다른 콜렉션 처럼 allCases 를 사용할 수 있음
- 콜렉션의 요소는 열거형 타입의 인스턴스 이기 때문에 이 경우에는 Beverage 값들
- 위의 예제는 얼마나 많은 케이스가 존재하는지 계산하고 아래의 예제는 for-in 루프를 사용하여 모든 케이스를 반복합니다.
연관값
- 상수 또는 변수를 Planet.earth 로 설정하고 나중에 값을 체크할 수 있음
- 그러나 경우에 따라 이러한 케이스 값과 함께 다른 타입의 값을 저장할 수 있는 것이 유용
- 이 추가적인 정보를 연관값 (associated value) 이라고 하며 해당 케이스를 코드에서 값으로 사용할 때마다 달라짐
- 주어진 타입의 연관된 값을 저장하기 위해 Swift 열거형을 정의할 수 있고, 이 값 타입은 필요에 따라 열거형의 각 케이스에 따라 달라질 수 있음
- 재고 추적 시스템이 2가지 타입의 바코드로 제품을 추적해야 된다고 가정
- 어떤 제품은 숫자 0 에서 9 를 사용하는 UPC 형식의 1D 바코드 라벨이 부착
- 각 바코드에는 숫자 시스템과 이어서 5자리의 제조업체 코드와 5자리의 제품 코드가 있음
- 다음에는 코드가 올바르게 스캔되었는지 확인하기 위해 검사 숫자가 있음
- 다른 제품은 어떠한 ISO 8859-1 문자도 사용할 수 있고 2,953개의 문자까지 인코딩할 수 있는 QR 코드 형식의 2D 바코드 라벨이 부착
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
- 재고 추적 시스템은 UPC 바코드를 4개의 정수로 된 튜플로 저장하고 QR 코드 바코드를 모든 길이의 문자열로 저장하는 것이 편리
- (Int, Int, Int, Int) 타입의 연관된 값을 가진 upc 또는 String 타입의 연관값을 가진 qrCode 를 취할 수 있는 Barcode 라는 열거형 타입을 정의
- 이 정의는 어떠한 실질적인 Int 또는 String 값을 제공하지 않음
- 이것은 단지 Barcode.upc 또는 Barcode.qrCode 와 같을 때 Barcode 상수와 변수에 저장할 수 있는 연관값의 타입을 정의
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
- 이러한 타입을 이용하여 새로운 바코드를 생성 가능
- productBarcode 라 불리는 새로운 변수를 생성하고 연관된 튜플값인 (8, 85909, 51226, 3) 을 Barcode.upc 의 값으로 할당
- 같은 상품의 다른 바코드 타입을 할당 가능
- 여기서 기존의 Barcode.upc 와 기존 변수의 정수 값은 새로운 Barcode.qrCode 와 해당 값의 문자열 값으로 대체
- Barcode 타입의 상수와 변수는 .upc 또는 .qrCode 모두 이것들과 연관된 값으로 저장할 수 있지만 오직 하나의 값만 저장할 수 있음
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 케이스의 본문 내에서 사용하기 위해 상수 (let 접두사) 또는 변수 (var 접두사)로 각 연관된 값을 추출
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."
- 열거형 케이스를 위한 연관된 값 모두 상수로 추출하거나 변수로 추출하려면 간결하게 케이스 이름 앞에 var 또는 let 을 선언
원시값
- 연관된 값의 대안으로 열거형 케이스는 모두 동일한 타입의 기본값 (원시값 (raw values))으로 미리 채워질 수 있음
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
- ASCIIControlCharacter 라 하는 열거형의 원시값은 Character 타입으로 정의되고 일반적인 ASCII 제어 문자 중 일부를 선정
- 원시값은 문자열, 문자, 정수 또는 부동소수점 숫자 타입이 가능
- 각 원시값은 열거형 선언부 내에서 유일한 값이어야 함
- 원시값은 연관값 (associated values)과 같지 않음
- 원시값은 위의 3개의 ASCII 코드처럼 코드에서 열거형을 처음 정의할 때 미리 설정하는 값
- 특정 열거형 케이스를 위한 원시값은 항상 같음
- 연관값은 열거형 케이스 중 하나를 기반으로 새로운 상수 또는 변수를 생성할 때 설정하고 달라질 수 있음
암시적으로 할당된 원시값
- 정수 또는 문자열 원시값이 저장된 열거형으로 동작 시 각 케이스에 명시적으로 원시값을 가질 필요가 없음
- 원시값을 설정하지 않으면 Swift는 자동적으로 값을 할당
- 예를 들어 정수를 원시값으로 사용하면 각 케이스의 암시적 값은 이전 케이스보다 하나씩 증가
- 첫번째 케이스에 값 설정이 안되어 있으면 0 으로 설정
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
- Planet.mercury 는 명시적으로 원시값 1 을 가지고 Planet.venus 는 암시적으로 원시값 2 를 가짐
-
원시값으로 문자열이 사용될 때 각 케이스의 암시적 값은 케이스의 이름의 문자
enum CompassPoint: String {
case north, south, east, west
}
- CompassPoint.south 는 "south" 의 암시적 원시값을 가지고 있음
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
- rawValue 프로퍼티를 사용하여 열거형 케이스의 원시값에 접근 가능
원시값으로 초기화
- 원시값 타입으로 열거형을 정의한다면 열거형은 원시값의 타입 (rawValue 라는 파라미터) 을 사용하는 초기화를 자동으로 수신하고 열거형 케이스 또는 nil 을 반환
- 이 초기화를 이용하여 열거형의 새 인스턴스를 만들 수 있음
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
- 7 원시값으로 천왕성을 식별
- 그러나 가능한 모든 Int 값으로 행성을 찾지 않기 때문에 원시값 초기화는 항상 옵셔널 열거형 케이스를 반환
- 위의 예제에서 possiblePlanet 은 Planet? 또는 옵셔널 Planet 타입
- 원시값 초기화는 모든 원시값을 열거형 케이스로 반환할 수 없으므로 초기화가 실패할 수 있음
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"
- 11 의 위치로 행성을 찾는다면 원시값 초기화로 부터 반환된 옵셔널 Planet 값은 nil
- 11 의 원시값으로 행성을 찾기위해 옵셔널 바인딩을 사용
- if let somePlanet = Planet(rawValue: 11) 구문은 옵셔널 Planet 을 생성하고 가져올 수 있다면 옵셔널 Planet 의 값을 somePlanet 에 설정
- 이 경우 11 의 위치로 행성을 가져올 수 없으므로 대신에 else 구문이 실행
재귀 열거형
- 재귀 열거형 (recursive enumeration) 은 열거형 케이스에 하나 이상의 연관값으로 열거형의 다른 인스턴스를 가지고 있는 열거형
- 열거형 케이스가 재귀적임을 나타내기 위해 케이스 작성 전에 indirect 를 작성하여 컴파일러에게 필요한 간접 (indirection) 계층을 삽입하도록 지시
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
- 간단한 산술 표현식을 저장하는 열거형의 예시
- 열거형 시작 전에 indirect 를 작성하여 연관값을 가진 모든 열거형 케이스에 간접을 활성화
- 이 열거형은 숫자, 2개의 표현식의 덧셈, 2개의 표현식의 곱셈의 3가지의 산술 표현식을 저장 가능
- addition 과 multiplication 케이스는 산술 표현식인 연관값을 가지고 있고 이 연관값은 중첩 표현식을 가능하게 해줌
- 예를 들어 (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))
- (5 + 4) * 2 에 대해 생성되는 ArithmeticExpression재귀 열거형
- 재귀 함수는 재귀 구조를 가진 데이터로 작업하는 간단한 방법
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"
- 산술 표현식을 판단하는 함수
- 이 함수는 단순하게 관련된 값을 반환하여 숫자를 판단
- 좌항의 식을 판단하고 우항의 식을 판단한 다음에 이를 더하거나 곱하여 덧셈 또는 곱셈을 판단
출처
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations
'iOS' 카테고리의 다른 글
Swift - Concurrency (0) | 2023.05.13 |
---|---|
SwiftUI - Search Bar 구현 (0) | 2023.05.11 |
Swift - Extensions (0) | 2023.03.05 |
Swift - Concurrency (동시성) (0) | 2023.02.26 |
Swift - Protocol(준수에 대한 검사, 상속, 옵셔널 프로토콜, 확장) (4/4) (0) | 2023.02.19 |
Comments