'enum'을 사용하여 enumeration(열거형)을 생성한다. class와 다른 명명된 유형들과 마찬가지로, enum은 해당 유형과 관련된 메서드를 가질 수 있다.
enum Rank: Int {
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace
let aceRawValue = ace.rawValue
기본적으로 Swift는 enum의 원시 값(raw values)을 0부터 시작해서 1씩 증가시켜 할당하지만, 원시 값을 명시적으로 지정하여 이 동작을 변경할 수 있다. 위의 예제에서는 Ace가 명시적으로 원시 값 1을 갖도록 지정되고, 나머지 원시 값들은 순서대로 할당된다. 또한 enum의 원시 유형으로 문자열 또는 부동 소수점 숫자를 사용할 수 있다. 원시 값에 접근하기 위해 'rawValue' 속성을 사용한다.
'init?(rawValue:)' 이니셜라이저를 사용하여 원시 값으로부터 열거형의 인스턴스를 만들 수 있다. 이 이니셜라이저는 원시 값과 일치하는 열거형 케이스를 반환하거나, 일치하는 Rank가 없는 경우 nil을 반환한다.
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
enum의 case 값들은 실제 값이며, 단순히 원시 값(raw values)을 다른 방식으로 작성한 것이 아니다. 실제로 의미 있는 원시 값이 없는 경우에는 제공할 필요가 없다. 즉, enum의 case 값은 해당 열거형 케이스를 나타내기 위한 실제 값이며, 원시 값과는 별개로 사용된다.
enum Suit {
case spades, hearts, diamonds, clubs
func simpleDescription() -> String {
switch self {
case .spades:
return "spades"
case .hearts:
return "hearts"
case .diamonds:
return "diamonds"
case .clubs:
return "clubs"
}
}
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()
위에서 주목해야 할 두 가지는 enum의 'hearts' 케이스를 참조하는 두 가지 방법이다. 'hearts' 상수에 값을 할당할 때, enum 케이스 'Suit.hearts'는 전체 이름으로 참조된다. 이는 상수의 명시적인 유형이 지정되지 않았기 때문이다. switch 내에서는 열거형 케이스가 이미 Suit임이 알려진 상황에서 '.hearts'와 같이 간소화된 형태로 참조된다. 값의 유형이 이미 알려진 경우 언제든 간소화된 형태를 사용할 수 있다.
enum이 원시 값(raw values)을 가지는 경우, 이러한 값은 선언의 일부로 결정되며, 이는 특정 열거형 케이스의 모든 인스턴스가 항상 동일한 원시 값을 가짐을 의미한다. 또 다른 선택 사항은 각 enum 케이스에 연결된 값을 가질 수 있는 것인데, 이러한 값은 인스턴스를 만들 때 결정되며, 각 열거형 케이스의 인스턴스마다 다를 수 있다. 연결된 값을 열거형 케이스 인스턴스의 저장된 속성처럼 동작하는 것으로 생각할 수 있다. 예를 들어, 서버에서 일출 및 일몰 시간을 요청하는 경우를 생각해 보겠다. 서버는 요청한 정보로 응답하거나, 잘못된 내용에 대한 설명으로 응답한다.
enum ServerResponse {
case result(String, String)
case failure(String)
}
let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")
switch success {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
print("Failure... \(message)")
}
// Prints "Sunrise is at 6:00 am and sunset is at 8:09 pm."
일출 및 일몰 시간이 ServerResponse 값에서 추출되는 방식을 주목하자. 이는 값과 switch 케이스를 일치시키는 일부의 일부로서 수행된다.
'struct'를 사용하여 structure(구조체)를 생성한다. 구조체는 메서드와 이니셜라이저를 포함하여 클래스와 많은 동작을 지원한다. 구조체와 클래스 간의 가장 중요한 차이점 중 하나는 구조체는 코드 내에서 전달될 때 항상 복사된다는 것이며 (value type), 클래스는 참조로 전달된다 (reference type). 이러한 차이점은 구조체와 클래스를 사용할 때 메모리 및 데이터 관리에 중요한 영향을 미친다.
예를 들어, 구조체는 항상 값 형식으로 전달되므로 한 곳에서 변경된 구조체는 기존 구조체에 영향을 미치지 않는데, 클래스는 참조 형식으로 전달되기 때문에 한 곳에서 변경된 경우 해당 변경 사항은 참조한 모든 위치에 영향을 미칠 수 있다.
따라서 구조체는 데이터를 전달하는 간단하고 안전한 방법을 제공하는데 유용하며, 클래스는 공유 데이터와 공통 상태를 관리하는 데 더 적합하다. 어떤 유형을 사용할지는 작업의 요구 사항과 목적에 따라 다르다.
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()