iOS/swift

[Swift 공식문서 정리] - 열거형 (Enumerations)

skyiOS 2022. 2. 5. 04:12
반응형

열거형(Enumerations)

열거형(Enumerations)은 연관성이 있는 값들을 모아놓은 것을 말합니다 .c와 Objectvie-C 가 Integer값들로 열거형을 구성한 것에 반해 Swift 에서는 case값이 Stirng, Character, Int, Float, Double과 같은 값일 수 있습니다. swift 에서의 열거형은 좀 더 융통성이 있어서 열거의 각 경우에 값을 꼭 제공할 필요는 없습니다.

열거형은 1급 클래스형( first-class types)이어서 계산된 프로퍼티를 제공하거나 초기화를 지정하거나, 초기 선언을 확장해 사용 할 수 있습니다.


열거형 문법(Enumerations Syntax)

열거형은 enum 키워드를 사용해 정의합니다.

enum CompassPoint {
    case north
    case south
    case east
    case west
}
C나 Objective-C 와는 다르게 Swift에서 열거형은 생성될 때 각 case별로 기본 integer값을 할당하지 않습니다.
위를 예로들면  north,south,east,west는 각각 암시적으로 0,1,2,3 값을 갖지 않습니다.
Swift에서 열거형의 각 case는 compassPoint으로 선언된 온전한 값입니다.
enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
여러 case를 콤마(,)로 구분해서 한 줄에 적을 수 있습니다.
각 열거형 정의는 완전 새로운 형(type)을 정의합니다.
Swift의 다른 형(type)과 마찬가지로 이름은 대문자로 시작해야 합니다.
var directionToHead = CompassPoint.west
위의 코드와 같이 열거형의 케이스에 접근할 수 있습니다.
directionToHead = .east
directionToHead 는 초기화 될때 타입추론을 통해 CompassPoint 형(type)을 갖게 됩니다.
한번 CompassPoint로 정의되면 다음에 값을 할당할 때 형을 생략하고  (.) 을 이용해 값을 할당할 수 있습니다.

Switch 구문에서 열거형 값 매칭하기
(Matching Enumeration Values with a Switch Statement)

각 열거형 값을 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 문을 사용 할 때 반드시 열거형의 모든 경우(cases)를 포함해야 합니다. 모든 경우를 포함하는 경우 default는 생략 가능합니다.
만약 열거형의 모든 경우(cases)를 포함하지 않는경우 default를 사용해야 합니다.

let somePlanet = Planet.earth
switch somePlanet {
case .earth:
    print("Mostly harmless")
default:
    print("Not a safe place for humans")
}
반응형

관련 값 (Associated Values)

열거형의 각 case에 custom type의 추가적인 정보를 저장할 수 있습니다.

어떠한 타입의 값도 열거형과 함께 사용될 수 있고 각각의 열거 케이스마다 다른 타입을 사용할 수도 있습니다. 이러한 열거형을 discriminated unions, tagged unions, variants in other programming language라고 합니다.

바코드가 위와같이 4가지 구분으로 이루어진 숫자로 이루어진 종류가 있거나, 2953개의 문자로 구성된 QR코드 형태로 이루어진 두 가지 종류가 있다면 아래와 같은 열거형으로 정의할 수 있습니다.

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

아래와 같이 각각의 타입마다 새로운 값을 생성할 수 있습니다. 

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
productBarcode 변수에는 한 시점에 하나의 값만 저장할 수 있습니다.
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 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."

위와 같이 switch와 함께 사용해서 해당 값에 있는 값을 추출할 수 있습니다. 
case 안의 관련 값이 전부 상수이거나 변수이면 공통된 값을 case 뒤에 선언해서 보다 간결하게 기술할 수 있습니다.


Raw 값( Raw Value)

raw Value는 기본값으로 미리 선언해 둘 수 있는 타입입니다.

enum ASCIIControlCharacter: Character {
    case tab = "\t"
    case lineFeed = "\n"
    case carriageReturn = "\r"
}

위 예제의 ASCIIControlCharacter 는 Character 타입으로 정의되었지만, String, Charater,Integer,Float등의 형을 사용할 수 있습니다. 각 rawValue는 열거형 선언에서 유일한 값으로 중복 되어서는 안됩니다.

RawValue는 관계값(Associated Value)과는 다릅니다. raw Value는 코드에서 열거형을 처음 선언할 때 정의되서 특정 열거형의 raw Value는 항상 같은 값을 갖습니다. 하지만 관계 값은 같은 case라도 생성될 때 달라질 수 있습니다.

암시적으로 할당된 Raw 값 (Implicitly Assigned Raw Values)

열거형을 다루면서 rawValue 으로 Integer나 String 값을 사용할 수 있는데, 각 case별로 명시적으로 rawValue를 할당할 필요는 없습니다.  만약 rawValue를 할당하지 않으면 Swift에서 자동으로 값을 할당해 줍니다.

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3

위의 코드를보면 mercury 에 1을 명시적으로 할당하면, 그 뒤에 케이스인 venus 는 2 earth는 3 과 같이 1씩 증가하며 할당하게 됩니다.

enum CompassPoint: String {
    case north, south, east, west
}

let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"

위의 코드처럼 아무것도 선언하지 않는경우 case의 이름이 String 타입으로 Raw Value를 할당하게 됩니다.

Raw 값을 이용한 초기화 (Initializing from a Raw Value)

rawValue를 이용해 열거형 변수를 초기화 할 수 있습니다.
반환값으로는 해당 rawValue가 존재하면 해당 case의 값이나옵니다.
해당 rawValue가 존재하지 않는 값이라면 nil이 반환됩니다.( 옵셔널 타입)
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
모든 rawValue에 대해 열거형 case반환이 보장되지 않으므로 실패할 수 있는 초기화 방법(failable initializer) 입니다.
따라서 if let구문을 이용한 옵셔널 바인딩을 사용해 안전하게 추출해야 합니다.
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"

재귀 열거형 (Recursive Enumerations)

재귀 열거형(Recursive Enumerations)는 다른 열거 인스턴스를 관계 값으로 갖는 열거형 입니다.

case앞에 indirect 키워드를 붙여 사용합니다.

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

관계 값을 갖는 모든 열거형 case에 indirect 표시를 하고 싶으면 enum 키워드 앞에 indirect 표시 하면 됩니다.

indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

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

실제 재귀 함수에서 사용할 수 있는 예는 아래와 같습니다.

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"

 

반응형