[Swift 프로그래밍] 열거형

이정훈·2023년 2월 9일
0

Swift 기본

목록 보기
22/22
post-thumbnail

본 내용은 스위프트 프로그래밍 3판 (야곰 지음) 교재를 공부한 내용을 바탕으로 작성 하였습니다.

열거형


열거형은 연관된 항목을 묶은 타입이라고 정의할 수 있다. Swift에서는 열거형 또한 하나의 타입으로 취급한다.

배열이나 딕셔너리, 집합 등 collection 타입은 var로 선언할 경우 내부 요소들을 수정할 수 있었지만, 열거형은 최소 선언시 선언한 항목들 이외에는 수정이 불가능하다.

따라서 열거형은 예상되는 값이 한정되어 있을때 제한된 선택지를 주고자하는 의도로 사용될 수 있다.

열거형을 선언할 때는 enum 키워드를 사용하며 기본적인 형태는 다음과 같다.

enum Number {
    case one
    case two
    case three
}

혹은 다음과 같이 쓸 수 있다.

enum Number {
    case one, two, three
}

열거형의 각 항목들은 그 자체가 하나의 값으로 사용되며, 열거형 타입의 변수를 선언하는 방법은 다음과 같다.

var num1: Number = Number.one
//var num1: Number = .one

이처럼 타입 추론을 사용하지 않고 변수의 타입을 명시해 준 경우 .one과 같이 열거형의 타입은 생략할 수 있다.

원시값(Raw Value)


열거형의 각 항목들은 그 자체로 하나의 값이지만 각 항목이 원시값(Raw Value)이라는 특정 값을 가질 수 있다. 원시값을 지정해 주기 위해서는 열거형 타입 이름 옆에 원시 값으로 가질 타입을 명시해 주고, rawValue 프로퍼티를 통해 해당 원시값에 접근할 수 있다.

enum Number: Int {    //rawValue: Integer type
    case one = 1
    case two = 2
    case three = 3
}

var num2: Number = Number.two
print("num2 = \(num2.rawValue)")    //num2 = 2

만약 열거형의 원시값을 이미 알고 있다면, 원시값을 통해 열거형 값을 지정할 수 있다.

var num3: Number? = Number(rawValue: 3)
print("num3 = \(num3!)")    //num3 = three

하지만 여기서 주의할 점은 올바르지 않은 원시값을 전달할 위험을 대비하여 반환 값은 항상 optional 타입이다.

원시값의 경우 일부 항목만 원시값을 가져도 상관 없다.(일부만 원시값을 가지는 경우 나머지 항목은 Swift가 알아서 지정한다.)

연관값


열거형의 각 항목이 자신과 연관된 값인 연관값을 가질 수 있다.

enum DayTodo {    //연관값: String
    case sun(todo: String)
    case mon(todo: String)
    case tue(todo: String)
    case wen(todo: String)
    case thu(todo: String)
    case fri(todo: String)
    case sat(todo: String)
}

var mondayTodo: DayTodo = DayTodo.mon(todo: "장보기")
print(mondayTodo)    //mon(todo: "장보기")

항목 순회(protocol 채택)


위에서 언급한 것과 같이 Swift는 열거형 또한 하나의 타입으로 취급한다. 따라서 열거형 또한 protocol을 채택할 수 있다.

열거형에 CaseIterable protocol을 채택하면 타입 프로퍼티인 allCases 프로퍼티를 사용할 수 있으며, 해당 타입 프로퍼티는 열거형의 모든 항목을 나열한 collection 타입을 반환한다.

enum Number: Int, CaseIterable {    //rawValue: Integer type
    case one = 1
    case two = 2
    case three = 3
    case four
    case five
}

var allCase: [Number] = Number.allCases
print(allCase)    //[Number.one, Number.two, Number.three, Number.four, Number.five]

만약 위와 같이 열거형이 원시값을 가진다면, 원시값 타입을 먼저 선언하고 그 뒤에 protocol을 채택 해준다.

하지만 아래와 같이 연관값을 가지는 열거형의 경우 단순히 CaseIterable 프로토콜을 채택하는 것만으로 allCases 사용할 수 없기 때문에 열거형 타입을 요소로 가지는 배열을 반환하는 allCases 타입 프로퍼티를 직접 구현해 주어야 한다.

enum DayTodo: CaseIterable {    //연관값: String
    case sun(todo: String)
    case mon(todo: String)
    case tue(todo: String)
    case wen(todo: String)
    case thu(todo: String)
    case fri(todo: String)
    case sat(todo: String)
    
    static var allCases: [DayTodo] {
        return [DayTodo.sun(todo: "쉬는 날"), DayTodo.mon(todo: "장보기"), DayTodo.tue(todo: "알바"),
        DayTodo.wen(todo: "친구 약속"), DayTodo.thu(todo: "알바"), DayTodo.fri(todo: "우편 보내기"),
        DayTodo.sat(todo: "영화 보기")]
    }
}

print(DayTodo.allCases)    /*[DayTodo.sun(todo: "쉬는 날"),
DayTodo.mon(todo: "장보기"),
DayTodo.tue(todo: "알바"),
DayTodo.wen(todo: "친구 약속"),
DayTodo.thu(todo: "알바"),
DayTodo.fri(todo: "우편 보내기"),
DayTodo.sat(todo: "영화 보기")]*/

순환 열거형


순환 열거형은 열거형의 연관값이 자기 자신 타입일때 사용하는 방법으로 indirect 키워드와 함께 사용한다.

enum Calculator {
    case number(Int)    //Integer type 연관값
    indirect case add(Calculator, Calculator)    //Calculator type 연관값
    indirect case mul(Calculator, Calculator)
}

위 처럼 필요한 부분에만 indirect 키워드를 사용해도 되고 아래 처럼 열거형 타입 전체에 indirect 키워드를 사용할 수 있다.

indirect enum Calculator {
    case number(Int)    //Integer type 연관값
    case add(Calculator, Calculator)    //Calculator type 연관값
    case mul(Calculator, Calculator)
}

다음은 순환 열거형을 사용하여 계산기를 구현한 코드이다.

let two: Calculator = .number(2)
let five: Calculator = .number(5)
let sum: Calculator = .add(two, five)
let multiply: Calculator = .mul(two, five)

func operate(_ express: Calculator) -> Int {
    switch express {
    case .number(let value):    //let binding
        return value
    case .add(let left, let right):
        return operate(left) + operate(right)
    case .mul(let left, let right):
        return operate(left) * operate(right)
    }
}

print(operate(sum))    //7
print(operate(multiply))    //5

비교 가능한 열거형


열거형이 Comparable protocol을 준수하거나 연관값이 Comparable protocol을 준수한다면 열거형의 각 항목들을 비교할 수 있고, 이때 대소비교는 먼저 선언된 항목이 더 작은 값이 된다.

enum AppleSilicon: Comparable {
    case m1
    case m1Pro(core: Int)
    case m1Max(core: Int)
    case m1Ultra
}

let mac: AppleSilicon = .m1
let myMac: AppleSilicon = .m1Pro(core: 8)
let myMacBook: AppleSilicon = .m1Pro(core: 10)
let yourMac: AppleSilicon = .m1Max(core: 10)
let yourMacStudio: AppleSilicon = .m1Ultra

var device: [AppleSilicon] = [mac, myMac, myMacBook, yourMac, yourMacStudio]
device.sort()
print(device)    //[AppleSilicon.m1, AppleSilicon.m1Pro(core: 8), AppleSilicon.m1Pro(core: 10), AppleSilicon.m1Max(core: 10), AppleSilicon.m1Ultra]
profile
새롭게 알게된 것을 기록하는 공간

0개의 댓글