Основы Swift / 16.2. Вложенные типы


Видео


Вложенные типы
Перечисления часто создаются для поддержки функциональности определённого класса или структуры. Аналогично, это может быть удобно для определения вспомогательных классов и структур чисто для использования внутри контекста более сложного типа. Для достижения этого Swift позволяет Вам определять вложенные типы, благодаря чему Вы можете вкладывать поддерживающие перечисления, классы и структуры внутри определи типа, который они поддерживают.

Чтобы вложить тип внутрь другого типа, напишите его определение внутри внешних фигурных скобок поддерживаемого типа. Типы могут быть вложены на столько много уровней, насколько это необходимо.
@{16.2\1\2}
Так как BlackjackCard - это структура без специальных конструкторов, то он получит неявно почленный конструктор. Вы можете использовать этот конструктор для инициализации новой константы с названием theAceOfSpades:
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
print("theAceOfSpades: \(theAceOfSpades.description)")
// Выведет "theAceOfSpades: suit is ♠, value is 1 or 11"
Хотя даже Rank и Suit вложены в BlackjackCard, их типы могут быть выведены из контекста, так что инициализация экземпляра может ссылаться на кейсы перечислений по их именам (.ace и .spades). В примере выше свойство description корректно сообщает, что туз пик имеет значение 1 или 11.
@{16.2\1\1}
Пример ниже определяет структуру с названием BlackjackCard, которая моделирует игральную карту для Блэкджека. Структура BlackjackCard содержит два вложенных перечисления с названиями Suit и Rank.

В блэкджеке тузы имеют значение один или одиннадцать. Эта особенность представляется с помощью структуры с названием Values, которая вложена в перечисление Rank:
struct BlackjackCard {

    // nested Suit enumeration
    enum Suit: Character {
        case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
    }

    // nested Rank enumeration
    enum Rank: Int {
        case two = 2, three, four, five, six, seven, eight, nine, ten
        case jack, queen, king, ace
        struct Values {
            let first: Int, second: Int?
        }
        var values: Values {
            switch self {
            case .ace:
                return Values(first: 1, second: 11)
            case .jack, .queen, .king:
                return Values(first: 10, second: nil)
            default:
                return Values(first: self.rawValue, second: nil)
            }
        }
    }
    // BlackjackCard properties and methods
    let rank: Rank, suit: Suit
    var description: String {
        var output = "suit is \(suit.rawValue),"
        output += " value is \(rank.values.first)"
        if let second = rank.values.second {
            output += " or \(second)"
        }
        return output
    }
}
Перечисление Suit описывает четыре основных игральных масти, включая чистое значение типа Character для представления их символов.

Перечисление Rank описывает тринадцать возможных ранков/ игровых карт, вместе с чистым Int-значением для представления их значения. (Это чисто значение не используется для Джека, Королевы, Короля и Туза.)

Как сказано выше, перечисление Rank определяет дальнейшую вложенную структуру Values. Эта структура инкапсулирует тот факт, что большинство карт имеют одно значение, но карта Туз имеет их два. Структура Values определяет два свойства для представления этого:

first типа Int
second типа Int? или "опциональный Int"

Rank также задаёт вычисляемое свойство, values, которое возвращает объект структуры Values. Это вычисляемое свойство определяет ранг карты и инициализует новый экземпляр типа Values с подходящими значениями, основываясь на его ранге. Он использует специальные значения для jack, queen, king, and ace. Для числовых карт он использует чисто значение Int.

Структура BlackjackCard имеет два свойства - rank и suit. Она также определяет вычисляемое свойство с названием description, которое использует значения, хранимые в rank и suit для постройки описания имени и значения карты. Свойство description использует опциональное связывание для проверки того, отображать ли второе значение, и если да, вставить дополнительное описание для этого второго значения.
Ссылки на вложенные типы
Чтобы использовать вложенный тип вне его определяющего контекста, претворите его имя именем типа, в который он вложен:
@{16.2\1\3}
let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
// heartsSymbol is "♡"
В примере выше, используются имена Suit, Rank и Values для краткости, так как их имена квалифицируются контекстом, в котором они определены.