일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 옵셔널
- reactnative android
- JavaScript
- client-side-rendering
- 동적언어
- 약타입
- switch구문
- 비반환함수
- reactnavigation
- 72410
- Server-Side-Rendering
- 데이터타입함수
- textinput
- 스위프트
- 중첩함수
- 함수형프로그래밍
- 17681
- next.js
- SWIFT
- ReactNative
- lineending
- 리액트네이티브
- ios
- multiline
- beforePopState
- 약타입언어
- replacingOccurrences
- 프로그래머스
- 나를부르는숲
- DelayInput
- Today
- Total
으니의 개발로그
[Swift] 데이터 타입 고급(3) - 열거형 본문
[Swift] 데이터 타입 고급(3) - 열거형
이 글은 Swift 프로그래밍 책을 읽고 요약한 내용입니다.
열거형 : 연관된 항목들을 묶어서 표현할 수 있는 타입
배열이나 딕셔너리 같은 타입과 다르게 프로그래머가 정의해준 항목 값 외에는 추가/수정이 불가함. 그렇기 때문에 딱 정해진 값만 열거형 값에 속할 수 있음
- 열거형이 요긴하게 사용되는 경우
- 제한된 선택지를 주고 싶을 때
- 정해진 값 외에는 입력받고 싶지 않을 때
- 예상된 입력 값이 한정되어 있을 때
- 열거형으로 묶을 수 있는 항목의 예
- 무선통신 방식 : WiFi, 블루투스, LTE, 3G, 기타
- 학생 : 초등학생, 중학생, 고등학생, 대학생, 대학원생, 기타
- 지역 : 강원도, 경기도, 경상도, 전라도, 제주도, 충청도
1. 기본 열거형
스위프트의 열거형은 enum
이라는 키워드로 선언할 수 있음
School 열거형의 선언
enum School {
case primary // 유치원
case elementary // 초등
case middle // 중등
case high // 고등
case college // 대학
case university // 대학교
case graduate // 대학원
}
각 항목은 그 자체가 고유의 값이며, 항목이 여러 가지라서 나열하기 귀찮거나 어렵다면 한 줄에 모두 표현해 줄 수 있음
enum School {
case primary, elementary, middle, high, college, university, graduate
}
School 열거형 변수의 생성 및 값 변경
var highestEducationLevel1: School = School.university
// 위 코드와 정확히 같은 표현
var highestEducationLevel2: School = .university
// 같은 타입인 School 내부의 항목으로만 highestEducationLevel의 값을 변경해줄수 있음
highestEducationLevel2 = .graduate
2. 원시 값
열거형의 강 항목은 자체로도 하나의 값이지만 항목의 원시 값(Raw Value)도 가질 수 있음. 즉, 특정 타입으로 지정된 값을 가질 수 있다는 뜻. 특정 타입의 값을 원시 값으로 가지고 싶으면 열거형 이름 오른쪽에 타입을 명시해주면 됨. 또, 원시 값을 사용하고 싶다면 rawValue라는 프로퍼티를 통해 가져올 수 있음
enum School: String {
case primary = "유치원"
case elementary = "초등학교"
case middle = "중학교"
case high = "고등학교"
case college = "대학"
case university = "대학교"
case graduate = "대학원"
}
let highestEducationLevel: School = School.university
print("저의 최종학력은 \(highestEducationLevel.rawValue) 졸업입니다.")
/* 저의 최종학력은 대학교 졸업입니다. */
enum WeekDays: Character {
case mon = "월", tue = "화", wed = "수", thu = "목", fri = "금", sat = "토", sun = "일"
}
let today: WeekDays = WeekDays.fri
print("오늘은 \(today.rawValue)요일입니다.")
/* 오늘은 금요일입니다. */
일부 항목만 원시 값을 주는 것도 가능. 문자열 형식의 원시 값을 지정해줬다면 각 항목 이름을 그대로 원시 값으로 갖게 되고, 정수 타입이라면 첫 항목을 기준으로 0부터 1씩 늘어난 값을 갖게 됨
enum School: String {
case primary = "유치원"
case elementary = "초등학교"
case middle = "중학교"
case high = "고등학교"
case college
case university
case graduate
}
let highestEducationLevel: School = School.university
print("저의 최종학력은 \(highestEducationLevel.rawValue) 졸업입니다.")
/* */
print(School.elementary.rawValue)
/* 초등학교 */
enum Numbers: Int {
case zero
case one
case two
case ten = 10
}
print("\(Numbers.zero.rawValue), \(Numbers.one.rawValue), \(Numbers.two.rawValue), \(Numbers.ten.rawValue)")
/* 0, 1, 2, 10 */
열거형이 원시 값을 갖는 열거형일 때, 열거형의 원시 값 정보를 안다면 원시 값을 통해 열거형 변수 또는 상수를 생서해줄 수 있음. 만약 올바르지 않은 원시 값을 통해 생성하려고 한다면 nil
반환
let primary = School2(rawValue: "유치원") // primary
let graduate = School2(rawValue: "석박사") // nil
let one = Numbers(rawValue: 1) // one
let three = Numbers(rawValue: 3) // nil
3. 연관 값
열거형 내의 항목(case)이 자신과 연관된 값을 가질 수 있음. 연관 값은 각 항목 옆에 소괄호로 묶어서 표현. 다른 항목이 연관 값을 갖는다고 모든 항목이 연관 갖을 가질 필요는 없음
// 연관 값을 갖는 열거형
enum MainDish {
case pasta(taste: String)
case pizza(dough: String, topping: String)
case chicken(withSauce: Bool)
case rice
}
var dinner: MainDish = MainDish.pasta(taste: "크림") // 크림 파스타
dinner = .pizza(dough: "치즈크러스트", topping: "불고기") // 불고기 치즈크러스트 피자
dinner = .chicken(withSauce: true) // 양념 통닭
dinner = .rice // 밥
여러 열거형의 응용
enum PastaTaste {
case cream, tomato
}
enum PizzaDough {
case cheeseCrust, thin, original
}
enum PizzaTopping {
case peperoni, cheese, bacon
}
enum MainDish {
case pasta(taste: PastaTaste)
case pizza(dough: PizzaDough, topping: PizzaTopping)
case chicken(withSauce: Bool)
case rice
}
var dinner: MainDish = MainDish.pasta(taste: PastaTaste.tomato)
dinner = MainDish.pizza(dough: PizzaDough.cheeseCrust, topping: PizzaTopping.bacon)
4. 항목 순회
열거형에 포함된 모든 케이스를 알아야 할 때 열거형의 이름 뒤에 콜론(:)
을 작성하고 한칸 띄운 뒤 CaseIterable
프로토콜을 채택해준다. 그러면 열거형에 allCases
라는 이름의 타입 프로퍼티를 통해 모든 케이스의 컬렉션을 생성해줌
enum School: CaseIterable {
case primary
case elementary
case middle
case high
case college
case university
case graduate
}
let allCases: [School] = School.allCases
print(allCases)
/* [School.primary, School.elementary, School.middle, School.high, School.college, School.university, School.graduate */
원시값을 갖는 열거형이라면 원시값 타입 다음에 쉼표(,)
를 쓰고 띄어쓰기를 한 후 CaseIterable 프로토콜을 채택해 주면 됨.
enum School: String, CaseIterable {
case primary = "유치원"
case elementary = "초등학교"
case middle = "중학교"
case high = "고등학교"
case college = "대학"
case university = "대학교"
case graduate = "대학원"
}
let allCases: [School] = School.allCases
print(allCases)
/* [School.primary, School.elementary, School.middle, School.high, School.college, School.university, School.graduate */
단순한 열거형에는 CaseIterable
프로토콜을 채택해 주는 것만으로 allCases
프로퍼티를 사용할 수 있다. 그렇지만 조금 복잡해지는 열거형은 그렇지 않을 수도 있다. 그 대표적인 예가 플랫폼별로 사용 조건을 추가하는 경우임
enum School: String, CaseIterable {
case primary = "유치원"
case elementary = "초등학교"
case middle = "중학교"
case high = "고등학교"
case college = "대학"
case university = "대학교"
@available(iOS, obsoleted: 12.0)
case graduate = "대학원"
static var allCases: [School] {
let all: [School] = [.primary, .elementary, .middle, .high, .college, .university]
#if os(iOS)
return all
#else
return all + [.graduate]
#endif
}
}
let allCases: [School] = School.allCases
print(allCases) // 실행환경에 따라 다른 결과
/* [School.primary, School.elementary, School.middle, School.high, School.college, School.university */
available
속성을 통해 특정 케이스를 플랫폼에 따라 사용할 수 있거나 없는 경우가 생기면 CaseIterable
프로토콜을 채택하는 것만으로는 allCases
프로퍼티를 사용할 수 없기 때문에 직접 allCases
프로퍼티를 구현해 주어야 함. 이렇게 CaseIterable
프로토콜을 채택하여도 allCases
프로퍼티를 바로 사용할 수 없는 경우가 또 있는데, 바로 열거형의 케이스가 연관 값을 갖는 경우임
enum PastaTaste: CaseIterable {
case cream, tomato
}
enum PizzaDough: CaseIterable {
case cheeseCrust, thin, original
}
enum PizzaTopping: CaseIterable {
case peperoni, cheese, bacon
}
enum MainDish {
case pasta(taste: PastaTaste)
case pizza(dough: PizzaDough, topping: PizzaTopping)
case chicken(withSauce: Bool)
case rice
static var allCases: [MainDish] {
return PastaTaste.allCases.map(MainDish.pasta) + PizzaDough.allCases.reduce([]) { (result, dough) -> [MainDish] in result + PizzaTopping.allCases.map { (topping) -> MainDish in MainDish.pizza(dough: dough, topping: topping)}}
+ [true, false].map(MainDish.chicken)
+ [MainDish.rice]
}
}
print(MainDish.allCases.count)
/* 14 */
print(MainDish.allCases) // 모든 경우의 연관 값을 갖는 케이스 컬렉션
5. 순환 열거형
순환 열거형은 열거형 항목의 연관 값이 열거형 자신의 값이고자 할 때 사용. 순환 열거형을 명시하고 싶다면 indirect
키워드를 사용하면 됨. 특정 항목에만 한정하고 싶다면 case
키워드 앞에 indirect
를 붙이면 되고, 열거형 전체에 적용하고 싶다면 enum
키워드 앞에 indirect
키워드를 붙이면 됨.
// 특정 항목에 순환 열거형 항목 명시
enum ArithmeticExpression1 {
case number(Int)
indirect case addition(ArithmeticExpression1, ArithmeticExpression1)
indirect case multiplication(ArithmeticExpression1, ArithmeticExpression1)
}
// 열거형 전체에 순환 열거형 명시
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 final = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case .number(let value):
return value
case .addition(let left, let right):
return evaluate(left) + evaluate(right)
case .multiplication(let left, let right):
return evaluate(left) * evaluate(right)
}
}
let result: Int = evaluate(final)
print("(5 + 4) * 2 = \(result)")
/* (5 + 4) * 2 = 18 */
6. 비교 가능한 열거형
Comparable
프로토콜을 준수하는 연관 값만 갖거나 연관 값이 없는 열거형은 Comparable
프로토콜을 채택하면 각 케이스를 비교할 수 있음. 앞에 위치한 케이스가 더 작은 값이 됨
enum Condition: Comparable {
case terrible
case bad
case good
case great
}
let myCondition: Condition = Condition.great
let yourCondition: Condition = Condition.bad
if myCondition >= yourCondition {
print("제 상태가 더 좋군요")
} else {
print("당신의 상태가 더 좋아요")
}
/* 제 상태가 더 좋군요 */
enum Device: Comparable {
case iPhone(version: String)
case iPad(version: String)
case macBook
case iMac
}
var devices: [Device] = []
devices.append(Device.iMac)
devices.append(Device.iPhone(version: "14.3"))
devices.append(Device.iPhone(version: "6.1"))
devices.append(Device.iPad(version: "10.3"))
devices.append(Device.macBook)
let sortedDevices: [Device] = devices.sorted()
print(sortedDevices)
/* [Device.iPhone(version: "14.3"), Device.iPhone(version: "6.1"), Device.iPad(version: "10.3"), Device.iMac] */
'Swift > 책 정리' 카테고리의 다른 글
[Swift] 조건문(1) - if 구문 (0) | 2021.01.20 |
---|---|
[Swift] 연산자(1) - 연산자의 종류 (0) | 2021.01.19 |
[Swift] 데이터 타입 고급(2-2) - 컬렉션에서 임의의 요소 추출과 뒤섞기 (2) | 2021.01.09 |
[Swift] 데이터 타입 고급(2-1) - 컬렉션형(배열, 딕셔너리, 세트) (0) | 2021.01.08 |
[Swift] 데이터 타입 고급(1) - 타입 별칭, 튜플 (0) | 2021.01.07 |