열거형 Enumerations

jane·2021년 10월 12일
0

Swift

목록 보기
2/7

열거형

서로 연관된 값들을 하나의 그룹으로 묶어 타입으로 만들 때 열거형을 쓴다.

열거형은 일급 객체이다.
전통적으로 클래스에서만 가능했던 계산 속성, 인스턴스 메서드, 이니셜라이저, 확장과 프로토콜도 사용 가능하다.

  • raw value 원시값
    - 원시값을 모든 case에 설정해주지 않아도 된다.
    - string, character, integer, float 타입... 가능
    • 원시값은 당연히 모든 케이스가 동일한 타입을 가져야한다.
  • associated values 연관값
    • 각 케이스별로 다른 형식의 연관값을 지정 가능 (자유로운 커스텀 가능)
    • 연관값을 상수나 변수에 바인딩 가능

열거형 문법

enum CompassPoint {
    case north
    case south
    case east
    case west
}
enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

이렇게 case를 각각 쓸 수도 있고, 쉽표로 연결하여 쓸 수도 있다.
첫글자는 대문자로, 이름은 단수형으로 짓는다.

var directionToHead = CompassPoint.west
directionToHead = .east

처음에 변수가 CompassPoint.west 로 초기화되고 나서 사용시에는 타입명을 생략해도 된다.

Swich 문과 찰떡

열거형은 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문은 Enum의 case들을 고려할 때 포괄적이어야 한다.
포괄적이라는 뜻은 모든 case를 포함해야 한다는 것이다. 한가지라도 빠지면 컴파일조차 안된다.
이런 제약사항때문에 switch문을 쓰게 되면 모든 케이스가 빠짐없이 있다는 사실을 보장한다.

default의 사용

만약 switch문에서 모든 열거형 케이스를 다 적어주지 못한다면 default를 사용해보자.
명시해주지 않은 케이스들을 한곳에 담아두게 된다.

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

CaseIterable

열거형의 모든 케이스들을 담은 컬렉션이 필요할 때
CaseIterable 키워드를 붙이면 (CaseIterable 프로토콜을 채택하여 그 안의 allCases 프로퍼티를 사용하는구나)
1. 모든 케이스를 담은 컬렉션이 제공되고
2. 그 컬렉션에 접근할 수 있도록 .allCases 프로퍼티가 제공된다.

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

열거형의 인스턴스들이 컬렉션에 들어가서 for-in 문으로 하나씩 꺼내올 수 있다.

연관값 Associated values

case가 카테고리의 역할로 바뀐다.
원시값과의 다른점?
원시값은 열거형을 선언할 때 구체적인 실제 정수형 "값"을 담지만, 연관값의 경우에는 그저 선언시 타입만 지정해주어서 값을 생성하는 시점에서 구체적인 값을 저장하게 된다.
각각의 케이스에는 다른 value 타입을 지정해줄 수 있다.

언제 사용?

  • 각 케이스별로 상이한 특징을 가지고 있을 때(케이스별로 다른 형식 사용 가능)
  • 정보가 바뀔 때마다 무한대로 케이스를 만들고 싶을 때
enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

두가지 종류의 바코드를 열거형으로 선언했는데, 각자 다른 연관값을 담는다.
선언시에는 구체적인 정보를 담지 않고 실제 사용시에 담는다.

var productBarcode = Barcode.upc(8, 85909, 51226, 3)

실제 바코드를 생성해보았다.

productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

같은 변수에 다른 타입의 바코드를 담아볼 수도 있다.

Barcode 타입의 변수는 .upc나 .qrCode 형태를 담을 수 있지만, 한번에 하나의 형태만 담을 수 있다.

연관값 내부의 값을 새로운 상수, 변수에 바인딩 가능

  • 열거형 내부에서 연관값을 사용하고 싶을 때
  • let, var 키워드 사용하여 추출
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).")
}

원시값 Raw Values

  • 특정 case의 원시값은 언제나 동일하다(변할 수 없다)
  • 주로 IntString 타입을 원시값으로 주로 사용한다.
  • 원시값은 Hashable한 모든 값을 사용 가능하다.
    -> (Int/String/Character/Bool등 가능하지만 주로 Int, String사용)

암시적으로 할당된 원시값

IntString 타입을 원시값으로 사용시, 각각의 케이스에 직접 값을 할당해주지 않아도 된다.
정수형의 경우 아무것도 할당되어있지 않을 때, 첫번째 케이스에 0이 할당되고 +1씩 되어 할당된다.
문자열의 경우에는 타입 지정 후 원시값을 할당하지 않는다면 그 케이스 자체가 원시값이 된다.

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

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

let earthsOrder = Planet.earth.rawValue //3
let sunsetDirection = CompassPoint.west.rawValue //"west"

원시값 초기화

profile
제가 나중에 다시 보려고 기록합니다 ✏️

0개의 댓글