Перечисления


Перечисление (enumeration) определяет общий тип для группы связанных значений. Причем сами объединенные в перечисление значения могут представлять любой тип - число, строку и так далее.

Для создания перечисления используется ключевое слово enum:

1
2
3
4
5
6
7
enum Season{

case Winter
case Spring
case Summer
case Autumn
}
Каждое отдельное значение в перечислении указывается после оператора case. В данном случае перечисление называется Season и представляет времена года и имеет четыре значения. То есть фактически Season представляет новый тип данных.

Также допустима сокращенная форма перечисления значений:

1
2
3
4
enum Season{

case Winter, Spring, Summer, Autumn
}
После определения перечисления мы сможем его использовать в программе:

1
var currentSeason = Season.Spring
Здесь переменная currentSeason представляет тип Season. Впоследствии мы можем присвоить этой переменной другое значение из Season:

1
var currentSeason = .Summer
Либо мы можем сначала определить переменную / константу типа перечисления, а потом ее инициализировать:

1
2
let lastSeason: Season
lastSeason = Season.Winter
С помощью конструкции switch можно узнать, какое значение содержит переменная / константа, представляющая перечисление:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum Season{

case Winter, Spring, Summer, Autumn
}

let currentSeason = Season.Spring

switch(currentSeason){

case .Winter:
print("Зима")
case .Spring:
print("Весна")
case .Summer:
print("Лето")
case .Autumn:
print("Осень")
}
Ассоциированные значения
С каждым значением в перечислении мы можем ассоциировать какое-либо другое значения, которое может представлять любой тип. При этом ассоциированные значения для разных значений одного и того же перечисления могут представлять различные типы.

Итак, пусть у нас перечисление представляет игрового персонажа:

1
2
3
4
5
enum Person{

case Human(String, Int)
case Elf(String)
}
Здесь перечисление определяет два возможных значения - двух игровых персонажей: человека (Human) и эльфа (Elf). Однако у человека мы можем задать два парамета: имя (String) и количество жизней (Int). А у эльфа нам нужен только один параметр - имя (String).

То есть в данном случае значение Person.Human будет ассоциировано с двумя значениями String и Int, а значение Person.Elf - с одним значением типа String:

1
2
var hero = Person.Human("Trogvar", 5)
hero = Person.Elf("Feonor")
С помощью конструкции switch мы также можем определить значение объекта:

1
2
3
4
5
6
7
8
switch(hero){
case .Human:
print("Вы играете человеком")
case .Elf:
print("Вы играете эльфом")
case .Gnom:
print("Вы играете гномом")
}
Но при необходимости мы также можем получить ассоциированные значения:

1
2
3
4
5
6
7
8
switch(hero){
case .Human (let name, let lives):
print("Вы играете человеком. Имя: \(name), количество жизней: \(lives)")
case .Elf (let name):
print("Вы играете эльфом. Имя: \(name)")
case .Gnom:
print("Вы играете гномом")
}
Для получения значений мы можем определить константы или переменные, в которые будут передаваться ассоциированные значения.

Чистые значения перечислений
Кроме ассоциированных значений члены перечисления могут иметь чистые значения (raw values). Например, пусть у нас есть перечисление, представляющее флагманы различных производителей:

1
2
3
4
5
6
enum Flagman: String{
case Samsung = "Galaxy S9"
case Apple = "iPhone X"
case Microsoft = "Lumia 950"
case Google = "Pixel 2"
}
При определении чистых значений нам надо указать их тип. В данном случае типом будет выступать тип String. Затем в программе мы сможем получить эти чистые значения с помощью свойства rawValue:

1
2
3
var myPhone = Flagman.Apple
print(myPhone) // Apple
print(myPhone.rawValue) // iPhone X
Если мы укажем тип прямых значений, но не укажем эти самые значения, то swift использует значения по умолчанию.

Если тип - String, то чистые значения будут представлять строковое представление элементов перечисления:

1
2
3
4
5
6
7
enum Flagman: String{

case Samsung, Apple, Microsoft, Google
}
var myPhone = Flagman.Apple
print(myPhone) // Apple
print(myPhone.rawValue) // Apple
Фактически будет совпадение между значениями перечисления и их чистыми значениями.

Если типом для чистых значений является тип Int, то элементы перечисления получат значения по порядку:

1
2
3
4
5
6
7
8
enum DayOfWeek: Int{

case Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

var currentDay = DayOfWeek.Wednesday
print(currentDay) // Wednesday
print(currentDay.rawValue) // 3
Первый элемент перечисления Monday=1 задает начальное значение для элементов перечисления. Если же мы не укажем его, то начальным значением будет 0.

Используя чистое значение, мы можем получить элемент перечисления:

1
2
3
4
5
6
7
enum DayOfWeek: Int{

case Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

var currentDay = DayOfWeek(rawValue: 7) // Optional(DayOfWeek.Sunday)
print(currentDay!)
В данном случае мы пытаемся получить элемент перечисления, который имеет чистое значение 7. Но данная операция возвращает не просто элемент перечисления, а объект Optional, то есть такой объект, который может иметь конкретное значение, а может иметь значение nil (отсутствие значения).

И если мы попытаемся получить, например, элемент с чистым значением 8, то мы получим nil. Поэтому мы можем применять условное выражение if для проверки полученного значения перед его использованием:

1
2
3
4
if let day = DayOfWeek(rawValue: 8){

print(day)
}
Методы перечислений
Как классы и структуры, перечисления могут определять методы. Например:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
enum DayOfWeek: Int{

case Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday

func getCurrentDay() -> String{

return DayOfWeek.getDay(number: rawValue)
}

static func getDay(number: Int) -> String{

switch number{

case 1:
return "Понедельник"
case 2:
return "Вторник"
case 3:
return "Среда"
case 4:
return "Четверг"
case 5:
return "Пятница"
case 6:
return "Суббота"
case 7:
return "Воскресенье"
default:
return "undefined"
}
}
}

var someDay: DayOfWeek = DayOfWeek.Sunday
someDay.getCurrentDay() // Воскресенье
var secondDay = DayOfWeek.getDay(number: 2) // Вторник
Свойства перечислений
Перечисления также могут иметь свойства, но это не могут быть хранимые свойства. А вычисляемые свойства вполне будут работать:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum DayOfWeek: Int{

case Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday

var label : String {
switch self {
case .Monday: return "Понедельник"
case .Tuesday: return "Вторник"
case .Wednesday: return "Среда"
case .Thursday: return "Четверг"
case .Friday: return "Пятница"
case .Saturday: return "Суббота"
case .Sunday: return "Воскресенье"
}
}
}
let day1 = DayOfWeek.Monday
print(day1.label) // Понедельник
print(DayOfWeek.Friday.label) // Пятница
В данном случае свойство label автоматически вычисляется на основании значения текущего объекта перечисления. Текущий объект перечисления можно получить с помощью ключевого слова self.

Инициализаторы в перечислениях
И также перечисления могут иметь иниицализаторы:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum DayOfWeek: Int{

case Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
init?(_ val:String) {

switch val {
case "Понедельник" : self = .Monday
case "Вторник": self = .Tuesday
case "Среда": self = .Wednesday
case "Четверг": self = .Thursday
case "Пятница": self = .Friday
case "Суббота": self = .Saturday
case "Воскресенье": self = .Sunday
case _ : return nil
}
}
}
let day1 = DayOfWeek("Пятница")
print(day1!.rawValue) // 5
В данном случае инициализатор принимает название дня недели и на его основании устанавливает значение текущего объекта. Если переданное название не распознано, то инициализатор возвращает nil.