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 |