티스토리 뷰

Swift 공식 가이드/Swift 2

Enumerations

찜토끼 2016. 3. 15. 13:47

Swift 3.0.1 가이드에 대응하는 정리글을 작성하였습니다!!!

Enumerations 정리 최신버전 링크 > http://wlaxhrl.tistory.com/41




Apple 제공 Swift 프로그래밍 가이드(2.2)의 Enumerations 부분을 공부하며 정리한 글입니다. 개인적인 생각도 조금 들어가있습니다.


들어가며

Enumeration(이하  ENUM)은 관련된 값들을 그룹으로 묶어서 common type 으로 정의해준다. ENUM을 이용하면 타입에 안전한(type-safe) 코드를 작성할 수 있다.

C에서의 ENUM은 각 케이스마다 Integer 값을 할당했었다. 이것을 raw value라고 한다. Swift에서의 ENUM은 raw value로 여러가지 타입을 사용할 수 있고 심지어 항상 raw value 를 할당하지 않아도 된다. 또한 케이스 각각에 연관된 값들을 지정할 수 있다.

Swift에서의 ENUM은 기존에는 class에서만 지원되던 기능들을 많이 제공해준다. 예를들면, 계산된 프로퍼티(computed properties), 인스턴스 메서드 등이 있다. 또한 ENUM의 이니셜라이저를 정의할 수도 있고, 기능을 확장시킬 수도 있고, 특정 프로토콜을 따르게 할 수도 있다. 자세한 것은 밑에서 살펴보자.



Enumeration Syntax

enum 키워드를 통해 정의할 수 있다.

case 키워드를 통하여 enumeration case 들을 정의할 수 있다.

enum CompassPoint { // 대문자로 시작하게끔
    case North // c, objective-c 와 다르게 자동으로 0부터 채워지지 않는다
    case South
    case East
    case West
}

enum Planet { 
    case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
    // 콤마로 구분하여 정의 가능
    // 복수형말고 단수형이 자명하다
}

이렇게 정의한 ENUM을 변수에 할당하는 법을 알아보자.

var directionToHead = CompassPoint.West
// 변수가 CompassPoint ENUM 타입이라고 유추되는 타이밍은
// West 케이스로 초기화가 될 때이다

directionToHead = .East
// 한 번 타입이 판단된 변수는 이런 식으로
// ENUM 이름을 생략하고 값을 할당할 수 있다



Matching Enumeration Values with a Switch Statement

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"


// <주의> ENUM의 모든 케이스를 포함할 수 있어야 한다.
// 그게 안 된다면 default 케이스를 사용하자.
let somePlanet = Planet.Earth
switch somePlanet {
case .Earth:
    print("Mostly harmless")
default:
    print("Not a safe place for humans")
}
// prints "Mostly harmless"



Associated Values

ENUM의 케이스 각각에 연관된 다른 타입의 값들을 저장할 수 있다. 따라서 필요한 추가정보를 저장할 수 있으며, ENUM 변수에 접근할 때마다 이 정보를 변경하는 것도 가능하다. 이 값을 associated value라고 부른다. associated value 의 타입과 개수는 케이스 별로 각각 상이하게 정의되어도 된다.

enum Barcode {
    case UPCA(Int, Int, Int, Int)
    case QRCode(String)
}


// 아래와 같이 사용
var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")
productBarcode = .QRCode // 컴파일에러


// let과 var로 associated value를 추출할 수 있다
switch productBarcode {
case .UPCA(let numberSystem, let manufacturer, let product, let check):
    print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case .QRCode(let productCode):
    print("QR code: \(productCode).")
}
// prints "QR code: ABCDEFGHIJKLMNOP."


// 튜플 자체를 한번에 let 또는 var로 받아도 된다
switch productBarcode {
case let .UPCA(numberSystem, manufacturer, product, check):
    print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .QRCode(productCode):
    print("QR code: \(productCode).")
}
// prints "QR code: ABCDEFGHIJKLMNOP."
// <참고> 만약 case let .UPCA(values): 로 받으면 values는 4개 요소로 구성된 튜플이다



Raw Values

C에서 했던 것처럼 Default value(Raw value)로 ENUM의 케이스를 채울 수도 있다. Raw value의 경우에는 값의 타입이 케이스마다 같아야 한다.

enum ASCIIControlCharacter: Character { // Character 라고 정의
    case Tab = "\t"
    case LineFeed = "\n" // 이 경우 raw value는 모두 Character여야함
    case CarriageReturn = "\r" // 그리고 케이스 별로 유니크해야 한다
}

raw value는 꼭 Integer 값이 아니라 String, Character, Integer, Floating-Point 등 여러 타입을 사용할 수 있다.

<note> Raw Value는 ENUM을 처음 정의한 순간부터 들어가있는 디폴트 값이고 Associated Value는 ENUM의 케이스를 가지고 변수/상수를 만들 때마다 셋팅이 되는 값이다. 이 두개는 다른 것이다.

<참고> enum 선언 시 rawValue의 타입 지정을 하지 않는 경우 케이스에 대고 rawValue를 부르지 못한다. 그런 멤버가 없다는 오류가 난다. (ex: ASCIIControlCharacter.Tab.rawValue 실패)


<Implicitly Assigned Raw Values>

명시적으로 전부 raw value를 할당하지 않더라도 컴파일러가 알아서 raw value를 할당해준다.

enum Planet: Int {
    case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
   // 0, 1, 2, 3, ...
}

enum Planet: Int {
    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
   // 1, 2, 3, 4, ...
}

enum Planet: Int {
    case Mercury=1, Venus, Earth, Mars=10, Jupiter, Saturn, Uranus, Neptune
    // 1, 2, 3, 10, 11, 12, 13, 14
}

enum Planet: Int {
    case Mercury, Venus = 6, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
   // 0, 6, 7, 8, 9, 10, 11, 12
}

enum Planet: Int {
    case Mercury, Venus = 0, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
    // 컴파일 에러 (raw value가 유니크하지 않다며)
    // 0, 0, 1, 2, ... 가 되니까
}

enum Planet: Int {
    case Mercury = 6, Venus = 5, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
    // 컴파일 에러 (raw value가 유니크하지 않다며)
    // 6, 5, 6, 7, 8, ... 이 되니까
}

raw value 타입이 String 인 경우에는 default Raw Value 값이 Case 이름 자체이다.

enum CompassPoint: String {
    case North, South, East, West
}

enum CompassPoint: String {
    case North, South = "North", East, West
    // 컴파일 에러
}


<Initializing from a Raw Value>

Raw Value가 있는 ENUM은 Raw Value를 파라미터로 넘기는 이니셜라이저를 사용할 수 있다.

let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet 의 타입은 Planet?

이렇게 nil이 될 수 있는 이니셜라이저를 failable initializer 라고 한다

//  옵셔널 바인딩을 함께 활용
let positionToFind = 9
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 9"



Recursive Enumerations

recursive enumeration 이란 한 개 이상 케이스의 associated value로 ENUM의 인스턴스를 받는 ENUM을 말한다. indirect 라는 키워드를 사용하여 만든다.

 // 다음과 같이 필요 케이스 앞에 indirect 키워드를 붙여도 되고
enum ArithmeticExpression {
    case Number(Int)
    indirect case Addition(ArithmeticExpression, ArithmeticExpression)
    indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)
}

// 이렇게 enum 옆에 indirect 를 명시하면 모든 case에 적용
indirect enum ArithmeticExpression {
    case Number(Int)
    case Addition(ArithmeticExpression, ArithmeticExpression)
    case Multiplication(ArithmeticExpression, ArithmeticExpression)
}

이것을 이용하는 계산기 예제를 보자.

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)
    }
}

// evaluate (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))

print(evaluate(product))
// prints "18"


'Swift 공식 가이드 > Swift 2' 카테고리의 다른 글

Properties  (1) 2016.04.13
Classes and Structures  (0) 2016.03.27
Closures  (0) 2016.03.12
Functions  (0) 2016.03.12
Control Flow  (0) 2016.03.03
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함