Статьи

Совет: Перечисления в Swift

Перечисления являются общим шаблоном проектирования во многих языках программирования. Хотя вы, возможно, знакомы с перечислениями в C и Objective-C, реализация перечислений в Swift значительно более мощная и гибкая. В этом кратком совете вы узнаете, что особенного в перечислениях в Swift, как их использовать в ваших проектах и ​​что делает их такими мощными.

Перечисления не новы и, конечно же, они не уникальны для Swift. Однако, если вы знакомы с перечислениями в C, то вам понравится мощный взгляд Swift на перечисления.

Если перечисления или перечисления являются новыми для вас, то вы можете не знать, что они могут предложить. В Swift перечисления являются первыми типами классов, которые определяют список возможных значений для этого типа.

Примером могут быть возможные состояния сетевого подключения. Возможные состояния могут быть:

  • несвязно
  • соединительный
  • связано

Мы могли бы добавить четвертое состояние для случая, когда состояние неизвестно. Имея в виду этот пример, давайте посмотрим, как определить и реализовать такое перечисление.

Как я уже сказал, перечисления являются первоклассными типами в Swift. Определение перечисления выглядит очень похоже на определение класса или структуры. В приведенном ниже примере мы определяем перечисление ConnectionState .

1
2
3
enum ConnectionState {
     
}

Имя перечисления предшествует ключевому слову enum за которым следует пара фигурных скобок. Перечисление ConnectionState определяет возможные состояния сетевого подключения. Чтобы определить эти состояния, мы добавляем значения членов или члены к определению перечисления. Определение значения члена всегда начинается с ключевого слова case .

1
2
3
4
5
6
enum ConnectionState {
    case Unknown
    case Disconnected
    case Connecting
    case Connected
}

В C или Objective-C приведенное выше перечисление будет выглядеть немного иначе, как показано в примере ниже. Каждое значение перечисления соответствует целому числу, например, ConnectionStateUnknown равно 0 , ConnectionStateDisconnected равно 1 и т. Д.

1
2
3
4
5
6
typedef enum : NSUInteger {
    ConnectionStateUnknown,
    ConnectionStateDisconnected,
    ConnectionStateConnecting,
    ConnectionStateConnected
} ConnectionState;

Это не правда в Swift. Члены перечисления не соответствуют автоматически целочисленным значениям. Члены перечисления ConnectionState сами являются значениями и имеют тип ConnectionState . Это делает работу с перечислениями более безопасной и более явной.

Можно явно указать значения членов перечисления. В следующем примере члены перечисления ConnectionState имеют необработанное значение типа Int . Каждому члену присваивается необработанное значение, соответствующее целому числу.

1
2
3
4
5
6
enum ConnectionState: Int {
    case Unknown = -1
    case Disconnected = 0
    case Connecting = 1
    case Connected = 2
}

Обратите внимание, что мы указываем тип необработанных значений в определении перечисления и что никакие два значения-члена не могут иметь одинаковое необработанное значение. Если мы указываем только значение для Unknown члена, Swift автоматически увеличивает значение Unknown члена и присваивает уникальные значения другим членам перечисления. Чтобы лучше проиллюстрировать это, приведенный ниже пример идентичен предыдущему определению перечисления ConnectionState .

1
2
3
4
5
6
enum ConnectionState: Int {
    case Unknown = -1
    case Disconnected
    case Connecting
    case Connected
}

Использование перечисления ConnectionState аналогично использованию любого другого типа в Swift. В следующем примере мы объявляем переменную connectionState и устанавливаем для нее значение ConnectionState.Connecting .

1
var connectionState = ConnectionState.Connecting

Значением connectionState является ConnectionState.Connecting а переменная имеет тип ConnectionState .

Вывод типа Swift очень удобен при работе с перечислениями. Поскольку мы объявили connectionState как тип ConnectionState , теперь мы можем назначить новое значение, используя сокращенный синтаксис точки для перечислений.

1
connectionState = .Connected

Использовать перечисления в операторе if или switch просто. Помните, что операторы switch должны быть исчерпывающими. При необходимости добавьте регистр по default .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
enum ConnectionState {
    case Unknown
    case Disconnected
    case Connecting
    case Connected
}
 
var connectionState = ConnectionState.Connecting
 
connectionState = .Connected
 
switch connectionState {
    case .Disconnected:
        println(«Disconnected»)
    case .Connecting:
        println(«Connecting»)
    case .Connected:
        println(«Connected»)
    default:
        println(«Unknown State»)
}

В следующем примере показано, как можно использовать перечисление ConnectionState . Также показано, как получить доступ к связанному значению члена перечисления. Функция canConnect принимает экземпляр ConnectionState и возвращает Bool .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
func canConnect(connectionState: ConnectionState) -> Bool {
    var result = false
     
    switch connectionState {
        case .Connected(let port):
            if port == 3000 {
                result = true
            }
        default:
            result = false
    }
     
    return result
}
 
let state = ConnectionState.Connected(3000)
 
if canConnect(state) {
    // …
}

Функция canConnect возвращает значение true только если экземпляр ConnectionState переданный в функцию, равен .Connected а его соответствующее значение равно Int равному 3000 . Обратите внимание, что соответствующее значение члена Connected доступно в операторе switch как константа с именем port , которую мы затем можем использовать в соответствующем case .

Другой неотъемлемой чертой перечислений Swift являются связанные значения. Каждый член перечисления может иметь ассоциированное значение. Связанные ценности очень гибки. Например, связанные значения разных членов одного и того же перечисления не обязательно должны быть одного типа. Посмотрите на следующий пример, чтобы лучше понять концепцию связанных значений.

1
2
3
4
5
6
enum ConnectionState {
    case Unknown
    case Disconnected
    case Connecting(Int, Double)
    case Connected(Int)
}

Unknown и Disconnected члены не имеют связанного значения. Элемент Connecting имеет ассоциированное значение типа (Int, Double) , указывающее номер порта и интервал ожидания соединения. У элемента Connected есть связанное значение типа Int , указывающее номер порта.

Важно понимать, что связанное значение связано или связано с членом перечисления. Значение участника остается неизменным. В следующем примере показано, как создать экземпляр ConnectionState со связанным значением.

1
var connectionState = ConnectionState.Connecting(3000, 30.0)

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

01
02
03
04
05
06
07
08
09
10
11
12
enum ConnectionState {
    case Unknown
    case Disconnected
    case Connecting(Int, Double)
    case Connected(Int)
     
    init () {
        self = .Unknown
    }
}
 
var connectionState = ConnectionState() // .Unknown

В этом примере мы инициализируем экземпляр перечисления ConnectionState без явного указания его значения. Однако в инициализаторе перечисления мы установили для экземпляра значение Unknown . В результате переменная connectionState равна ConnectionState.Unknown .

Как и структуры, перечисления являются типами значений, что означает, что перечисление передается не по ссылке, как экземпляры классов, а по значению. Следующий пример иллюстрирует это.

1
2
3
4
5
6
7
var connectionState1 = ConnectionState()
var connectionState2 = connectionState1
 
connectionState1 = .Connected(1000)
 
println(connectionState1) // .Connected(1000)
println(connectionState2) // .Unknown

Несмотря на то, что мы присваиваем connectionState1 для connectionState2 , значения connectionState1 и connectionState2 отличаются в конце примера.

Когда connectionState1 назначается для connectionState2 , Swift создает копию connectionState1 и назначает его для connectionState2 . Другими словами, connectionState1 и connectionState2 ссылаются на два разных экземпляра ConnectionState .

Перечисления в Swift являются невероятно мощными по сравнению, например, с перечислениями в C. Один из самых мощных аспектов перечислений заключается в том, что они являются первоклассными типами в Swift. Безопасность типов является ключевым аспектом языка Swift, и перечисления идеально вписываются в это мышление.