Переменные и константы — это хранилища данных в памяти компьютера, в которых находятся конкретные значения. Каждая переменная или константа может содержать в себе значение определенного типа: целое число, дробное число, строку, отдельный символ, логическое значение или какой-либо иной объект. Со значениями каждого типа можно совершать только определенные операции, соответствующие типу данных. К примеру, числа можно складывать или вычитать, строки — объединять между собой и т.д.
Тип данных определяет, какая именно информация может храниться в параметре, а также какие операции можно производить. Если вам необходимо хранить данные текстового типа, то и параметр должен иметь текстовый тип данных, для целочисленных значений используют параметры с целочисленным типом данных и т.п.
В Swift, по аналогии с Objective–C, а также с другими языками программирования, есть ряд предустановленных базовых (как их называет Apple, фундаментальных) типов данных.
При объявлении переменной или константы обязательно должен быть указан тип данных, которые будут храниться в этом параметре. У вас может возникнуть вопрос, каким образом был определен тип данных в предыдущих листингах книги. Переменные и константы создавались, но каких-либо специальных конструкций для этого не использовалось. Swift — умный, если можно так выразиться, язык программирования. В большинстве случаев он определяет тип объявляемого параметра автоматически на основании инициализированного ему значения.
Для всех рассматриваемых в этой главе типов данных (фундаментальных типов) инициализируемые значения называются литералами: строковый литерал, целочисленный литерал и т.д. Теперь поговорим об этом подробнее.
Рассмотрим следующий пример из листинга 5.1.
Листинг 5.1
// объявим переменную и присвоим ей строковое значение
var someText = "Я учу Свифт"
Данный пример может быть прочитан следующим образом:
Объявить переменную с именем someText и присвоить ей значение «Я учу Свифт».
При этом не требуется дополнительно указывать, какие именно данные будут храниться в переменной, то есть не требуется указывать ее тип данных. Это связано с тем, что Swift самостоятельно анализирует инициализируемое значение, после чего принимает решение о типе параметра.
Основываясь на этом, прочитаем код предыдущего листинга следующим образом:
• Объявить переменную строкового типа с именем someText и присвоить ей строковое значение "Я учу Свифт".
Или по-другому:
• Объявить переменную типа String с именем someText и присвоить ей строковое значение "Я учу Свифт".
String — это ключевое слово, используемое в языке Swift для указания на строковый тип данных. Он позволяет хранить в переменных и константах произвольный текст. Подробные приемы работы с данным типом будут рассмотрены далее.
Операция, в которой Swift самостоятельно определяет тип объявляемого параметра, называется неявным определением типа.
В противовес неявному определению существует явное, когда разработчик сам указывает тип данных объявляемого параметра. При явном (непосредственном) определении типа переменной или константы после ее имени через двоеточие с помощью ключевого слова следует указать требуемый тип данных.
Рассмотрим еще несколько примеров объявления переменных со строковым типом данных (листинг 5.2).
Листинг 5.2
// создаем переменную orksName с неявным определением типа String
var orksName = "Рухард"
// создаем переменную elfsName с явным определением типа String
var elfsName: String = "Эанор"
// изменим значения переменных
orksName = "Гомри"
elfsName = "Лиасель"
В данном примере все переменные имеют строковый тип данных String, то есть могут хранить в себе строковые значения (текст).
У любой объявленной переменной или константы должен быть определен тип и указано первоначальное значение, иначе будет показано сообщение об ошибке (листинг 5.3).
Листинг 5.3
var firstHobbitsName // ОШИБКА: Type annotation missing in pattern (пропущено указание типа данных)
var secondHobbitsName: String //ОШИБКА: Variables currently must have an initial value (параметр должен иметь инициализируемое значение)
ПРИМЕЧАНИЕ Swift — язык со строгой типизацией. Однажды определив тип данных переменной или константы, вы уже не сможете его изменить. В каждый момент времени вы должны иметь четкое представление о типах значений, с которыми работает ваш код.
Все фундаментальные типы данных (строковые, числовые, логические и т.д.) называются фундаментальными типами. Они являются значимыми (value type), то есть их значения передаются копированием. Мы уже говорили об этом ранее. Напомню, что помимо значимых типов также существуют ссылочные типы данных (reference type).
При передаче значения переменной или константы значимого типа в другую переменную или константу происходит копирование этого значения, а при передаче значения ссылочного типа передается ссылка на область в памяти, в которой хранится это значение. В первом случае мы получаем два независимых параметра, а во втором — две ссылки на одно и то же значение в памяти.
Рассмотрим еще один пример работы со значимым типом данных (листинг 5.4).
Листинг 5.4
// неявно определим целочисленный тип данных
var variableOne = 23
// явно определим целочисленный типа данных и произведем копирование
// значения
var variableOneCopy: Int = variableOne
print(variableOneCopy)
// изменим значение в первой переменной
variableOne = 25
print(variableOneCopy)
print(variableOne)
Консоль:
23
23
25
ПРИМЕЧАНИЕ Int — это числовой тип данных, немного позже мы подробно его рассмотрим.
В данном примере хранилище variableOne — значимого типа. При передаче значения, хранящегося в variableOne, в новую переменную variableOneCopy произойдет создание полной независимой копии. Никакие изменения, вносимые в variableOne, не повлияют на значение, хранящееся в variableOneCopy.
Со ссылочным типом данных все иначе. Скопировав значение такого типа в другую переменную, вы получите ссылку на исходный объект, и если будете проводить изменения в ней, то изменения отразятся и в исходном объекте. Примеры ссылочных типов мы рассмотрим в следующих главах.
Работа с числами является неотъемлемой частью практически любой программы, и для этого в Swift есть несколько фундаментальных типов данных. Некоторые из них позволяют хранить целые числа, а некоторые — дробные.
Целые числа — это числа, у которых отсутствует дробная часть, например 81 или 18. Целочисленные типы данных бывают знаковыми (ноль, положительные и отрицательные значения) и беззнаковыми (только ноль и положительные значения). Swift поддерживает как знаковые, так и беззнаковые целочисленные типы данных. Для указания значения числовых типов используются числовые литералы.
Числовой литерал — это фиксированная последовательность цифр, начинающаяся либо с цифры, либо с префиксного оператора «минус» или «плюс». Так, например, «2», «–64», «+18» — все это числовые литералы.
Для объявления переменной или константы целочисленного типа необходимо использовать ключевые слова Int (для знаковых) и UInt (для беззнаковых).
Рассмотрим пример в листинге 5.5.
Листинг 5.5
// объявим переменную знакового целочисленного типа и присвоим
// ей значение
var signedNum: Int = -32
// объявим константу беззнакового целочисленного типа
// и проинициализируем ее значением
let unsignedNum: UInt = 128
В результате выполнения кода вы получите переменную signedNum целочисленного знакового типа Int со значением –32, а также константу unsignedNum целочисленного беззнакового типа UInt.
Разница между знаковыми и беззнаковыми типами заключается в том, что значение знакового типа данных может быть в интервале от –N до +N, а беззнакового — от 0 до +2N, где N определяет разрядность выбранного типа данных.
В Swift существуют дополнительные целочисленные типы данных: Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64 и UInt64. Они определяют диапазон возможных хранимых в переменных значениях: 8-, 16-, 32- и 64-битные числа. В табл. 5.1 приведены минимальные и максимальные значения каждого из перечисленных типов.
Таблица 5.1
Тип данных | Int | Int8 | Int16 | Int32 |
Минимальное значение | –9223372036854775808 | –128 | –32768 | –2147483648 |
Максимальное значение | 9223372036854775807 | 127 | 32767 | 2147483647 |
Тип данных | Int64 | UInt | ||
Минимальное значение | –9223372036854775808 | 0 | ||
Максимальное значение | 9223372036854775807 | 9223372036854775807 |
Тип данных | UInt8 | UInt16 | UInt32 | UInt64 |
Минимальное значение | 0 | 0 | 0 | 0 |
Максимальное значение | 255 | 65535 | 4294967295 | 9223372036854775807 |
ПРИМЕЧАНИЕ Все приведенные выше целочисленные типы данных — это разные типы данных, и значения в этих типах не могут взаимодействовать между собой напрямую.
Все операции в Swift должны происходить между значениями одного и того же типа данных! Это очень важно!
Код, приведенный ниже, вызовет ошибку, так как производится попытка инициализации значения отличающегося типа.
var someNum: Int = 12
var anotherNum: Int8 = 14
someNum = anotherNum // ОШИБКА: Cannot assign value of type 'Int8' to
// type 'Int' (Нельзя передать значение типа Int8
// в Int)
Swift обладает одной особенностью: всё в этом языке программирования является объектами.
Объект — это некоторая сущность, реализованная с помощью программного кода. Например, объектом является цифра «2» или «продуктовый автомат», если, конечно, он описан языком программирования. Каждая сущность может обладать набором характеристик (называемых свойствами) и запрограммированных действий (называемых методами). Каждое свойство и каждый метод имеют имя, позволяющее использовать их. Так, например, у объекта «продуктовый автомат» могло бы существовать свойство «вместимость» и метод «выдать товар», а у целого числа «2» — свойство «максимально возможное хранимое число» и метод «преобразовать в строку».
Доступ к свойствам и методам в Swift осуществляется с помощью их имен, написанных через точку после имени объекта, к примеру:
ПродуктовыйАвтомат.вместимость = 490
ПродуктовыйАвтомат.выдатьТовар()
ПРИМЕЧАНИЕ Ранее мы встречались с понятием функции. Метод — это та же функция, но описанная и используемая только в контексте определенного объекта.
Так как «всё — это объект», то и любая переменная числового типа данных также является объектом. Каждый из рассмотренных ранее числовых типов описывает сущность «целое число». Разница между ними состоит в том, какие максимальное и минимальное числа могут хранить переменные этих типов данных. Вы можете получить доступ к этим характеристикам через свойства min и max. Для примера получим максимально и минимально возможные значения для типов Int8 и UInt8. (листинг 5.6)
Листинг 5.6
// минимальное значение параметра типа Int8
var minInt8 = Int8.min // -128
// максимальное значение параметра типа Int8
var maxInt8 = Int8.max // 127
// минимальное значение параметра типа UInt8
var minUInt8 = UInt8.min // 0
// максимальное значение параметра типа UInt8
var maxUInt8 = UInt8.max // 255
Так как тип данных UInt8 является беззнаковым и не предназначен для хранения отрицательных чисел, то и максимально возможное значение будет в два раза больше, чем у знакового «аналога» Int8.
Рассматриваемые приемы относятся к объектно-ориентированному программированию (ООП), с которым вы, возможно, встречались в других языках. Подробнее объекты и их возможности будут рассмотрены далее.
ПРИМЕЧАНИЕ Запомните, что среди целочисленных типов данных Apple рекомендует использовать только Int и UInt, но для тренировки мы поработаем и с остальными.
Даже такой простой тип данных, как целые числа, в Swift наделен широкими возможностями. В дальнейшем вы узнаете о других механизмах, позволяющих обрабатывать различные числовые значения.
Помимо целых чисел, при разработке приложений вы можете использовать числа с плавающей точкой, то есть числа, у которых присутствует дробная часть. Примерами могут служить числа 3.14 и —192.884022.
В данном случае разнообразие типов данных, способных хранить дробные числа, не такое большое, как при работе с целочисленными типами. Для объявления параметров, которые могут хранить числа с плавающей точкой, используются два ключевых слова: Float и Double. Оба типа данных являются знаковыми, то есть могут хранить положительные и отрицательные значения.
• Float — это 32-битное число с плавающей точкой, содержащее до 6 чисел в дробной части.
• Double — это 64-битное число с плавающей точкой, содержащее до 15 чисел в дробной части.
Разница этих типов данных состоит в точности: поскольку Double позволяет хранить больше знаков в дробной части, то и используется он лишь при необходимости выполнения расчетов высокой точности.
Пример объявления параметров приведен в листинге 5.7.
Листинг 5.7
// дробное число типа Float с явным указанием типа
var numFloat: Float = 104.3
// дробное число типа Double с явным указанием типа
let numDouble: Double = 8.36
// дробное число типа Double с неявным указанием типа
var someNumber = 8.36
Обратите внимание, что тип константы someNumber задается неявно (с помощью переданного дробного числового значения). В таком случае Swift всегда самостоятельно устанавливает тип данных Double.
ПРИМЕЧАНИЕ Значения типа дробных чисел не могут начинаться с десятичной точки. Я обращаю на это внимание, потому что вы могли видеть подобный подход в других языках программирования. В связи с этим следующая конструкция вызовет ошибку:
var variableErr1 = .12 // ОШИБКА: .12' is not a valid floating point literal; it must be written '0.12' (0.12 не является корректным числом с плавающей точкой)
Ранее мы познакомились с типами данных, позволяющими хранить числовые значения в переменных и константах. С числами, которые мы храним, можно проводить различные арифметические операции. Swift поддерживает то же множество операций, что и другие языки программирования. Каждая арифметическая операция выполняется с помощью специального оператора. Вот список доступных в Swift операций и операторов:
+ Бинарный оператор сложения складывает первый и второй операнды и возвращает результат операции (a + b). Тип результирующего значения соответствует типу операндов.
var res = 1 + 2 // 3
+ Унарный оператор «плюс» используется в качестве префикса, то есть ставится перед операндом (+a). Возвращает значение операнда без каких-либо изменений. На практике данный оператор обычно не используется.
var res = +3
- Бинарный оператор вычитания вычитает второй операнд из первого и возвращает результат операции (a — b). Тип результирующего значения соответствует типу операндов.
var res = 4 — 3 // 1
- Унарный оператор «минус» используется в качестве префикса, то есть ставится перед операндом (-a). Инвертирует операнд и возвращает его новое значение.
var res = -5
ПРИМЕЧАНИЕ Символы «минус» и «плюс» используются для определения двух операторов (каждый из них). Данная практика должна быть знакома вам еще со времен уроков математики, когда с помощью минуса вы имели возможность определить отрицательное число и выполнить операцию вычитания.
* Бинарный оператор умножения перемножает первый и второй операнды и возвращает результат операции (a * b). Тип результирующего значения соответствует типу операндов.
var res = 4 * 5 // 20
/ Бинарный оператор деления делит первое число на второе и возвращает результат операции (a / b). Тип результирующего значения соответствует типу операндов.
var res = 20 / 4 // 5
% Бинарный оператор вычисления остатка от деления двух целочисленных значений. Тип результирующего значения соответствует типу операндов.
var res = 19 % 4 // 3
ПРИМЕЧАНИЕ В третьей версии Swift был изменен подход к использованию некоторых операторов. К примеру, довольно часто используемые в программировании операторы инкремента (++) и декремента (—), увеличивающие и уменьшающие значение на единицу соответственно, были удалены из языка.
Перечисленные операторы можно использовать для выполнения математических операций с любыми числовыми типами данных (целочисленными или с плавающей точкой). Важно помнить, что типы значений, участвующих в операции, должны быть одинаковыми.
Чтобы продемонстрировать использование данных операторов, создадим две целочисленные переменные (листинг 5.8).
Листинг 5.8
// целочисленные переменные
var numOne = 19
var numTwo = 4
// переменные типа числа с плавающей точкой
var numThree = 3.13
var numFour = 1.1
Для первых двух переменных неявно задан целочисленный тип данных Int, для вторых двух неявно задан тип Double. Рассмотренные ранее операторы позволят выполнить арифметические операции с объявленными переменными (листинг 5.9).
Листинг 5.9
// операция сложения
var sum = numOne + numTwo // 23
// операция вычитания
var diff = numOne - numTwo // 15
// операция умножения
var mult = numOne * numTwo // 76
// операция деления
var qo = numOne / numTwo // 4
Каждый из операторов производит назначенную ему операцию над переданными ему операндами.
Вероятно, у вас возник вопрос относительно результата операции деления. Подумайте: каким образом при делении переменной firstNum, равной 19, на переменную secondNum, равную 4, могло получиться 4? Ведь при умножении значения 4 на secondNum получается вовсе не 19. По логике, результат деления должен был получиться равным 4,75!
Ответ кроется в типе данных. Обе переменные имеют целочисленный тип данных Int, а значит, результат любой операции также будет иметь тип данных Int. При этом у результата деления просто отбрасывается дробная часть и никакого округления не происходит.
ПРИМЕЧАНИЕ Повторю, что операции можно проводить только между переменными или константами одного и того же типа данных. При попытке выполнить операцию между разными типами данных Xcode сообщит об ошибке.
Рассмотренные операции будут работать в том числе и с дробными числами (листинг 5.10).
Листинг 5.10
// операция сложения
var sumD = numThree + numFour // 4,23
// операция вычитания
var diffD = numThree — numFour // 2,03
// операция умножения
var multD = numThree * numFour // 3,443
// операция деления
var qoD = numThree / numFour // 2,84545454545455
Так как типом данных исходных значений является Double, то и результату каждой операции неявно будет определен тот же тип данных.
Выполнение арифметических операций в Swift ничем не отличается от выполнения таких же операций в других языках программирования.
Также в Swift существует функция вычисления остатка от деления, при которой первый операнд делится на второй и возвращается остаток от этого деления. Или, другими словами, программа определяет, как много значений второго операнда поместится в первом, и возвращает значение, которое осталось, — оно называется остатком от деления. Рассмотрим пример в листинге 5.11.
Листинг 5.11
// целочисленные переменные
var numOne = 19
var numTwo = 4
// операция получения остатка от деления
var res1 = numOne % numTwo // 3
var res2 = -numOne % numTwo // -3
var res3 = numOne % -numTwo // 3
Распишем подробнее вычисление значения переменной res1 (остаток от деления numOne на numTwo).
numOne — (numTwo * 4) = 3
// Подставим значения вместо имен переменных
// и произведем расчеты
19 — (4 * 4) = 3
19 — 16 = 3
3 = 3
Другими словами, в numOne можно поместить 4 значения numTwo, а 3 будет результатом операции, так как данное число меньше numTwo.
Таким образом, остаток от деления всегда меньше делителя.
В листинге 5.11 при вычислении значений переменных res2 и res3 используется оператор унарного минуса. Обратите внимание, что знак результата операции равен знаку делимого, то есть когда делимое меньше нуля, результат также будет меньше нуля.
При использовании оператора вычисления остатка от деления (%) есть одно ограничение: он используется только для целочисленных значений.
Для вычисления остатка от деления дробных чисел используется метод truncatingRemainder(dividingBy:), который применяется к делимому (то есть пишется через точку после числа, которое требуется разделить). Имя параметра dividingBy внутри скобок указывает на то, что данный метод имеет входной аргумент, то есть значение, которое будет передано ему при его вызове и использовано в ходе работы метода. Пример использования метода приведен в листинге 5.12.
Листинг 5.12
// дробные переменные
var numberOne: Float = 3.14
var numberTwo: Float = 1.01
// операция получения остатка от деления
var result1 = numberOne.truncatingRemainder(dividingBy: numberTwo) // 0.1100001
var result2 = -numberOne.truncatingRemainder(dividingBy: numberTwo) // -0.1100001
var result3 = numberOne.truncatingRemainder(dividingBy: -numberTwo) // 0.1100001
Мы применили метод truncatingRemainder(dividingBy:) к переменной numberOne типа Float, а в качестве значения аргумента dividingBy передали переменную numberTwo.
Операция вычисления остатка от деления очень удобна в тех случаях, когда необходимо проверить, является ли число четным или кратным какому-либо другому числу.
Как определить четность? Очень просто: делите число на 2, и если остаток равен 0, то оно четное. Но для этой задачи можно использовать специальный метод isMultiple(of:), применяемый к анализируемому числу.
Как неоднократно говорилось, при проведении операций (в том числе арифметических) в Swift вы должны следить за тем, чтобы операнды были одного и того же типа. Тем не менее бывают ситуации, когда необходимо провести операцию со значениями, которые имеют разный тип данных. При попытке непосредственного перемножения, например, Int и Double, Xcode сообщит об ошибке и остановит выполнение программы.
Такая ситуация не осталась вне поля зрения разработчиков Swift, поэтому в языке присутствует специальный механизм, позволяющий преобразовывать одни типы данных в другие. Данный механизм называется приведением (от слова «привести»), выполнен он в виде множества глобальных функций.
ПРИМЕЧАНИЕ Справедливости ради стоит отметить, что на самом деле приведенные далее глобальные функции являются специальными методами-инициализаторами типов данных. Ранее мы говорили, что любые значения — это объекты и у них существуют запрограммированные действия, называемые методами. У каждого типа данных есть специальный метод, называемый инициализатором. Он автоматически вызывается при создании нового объекта, а так как в результате вызова объекта «числовой тип данных» создается новый объект — «число», то и метод-инициализатор срабатывает.
Инициализатор имеет собственное фиксированное обозначение — init(), и для создания нового объекта определенного типа данных он может быть вызван следующим образом:
ИмяТипаДанных.init(_:)
например:
var numObj = Int.init(2) // 2
В результате будет создана переменная numObj целочисленного знакового типа Int со значением 2.
С помощью вызова метода init(_:) создается новый объект, описывающий некую сущность, соответствующую типу данных (число, строка и т.д.). Swift упрощает разработку, позволяя не писать имя метода-инициализатора:
ИмяТипаДанных(_:)
например:
var numObj = Int(2) // 2
В результате выполнения данного кода также будет объявлена переменная типа Int со значением 2.
В будущем мы очень подробно разберем, что такое инициализаторы и для чего они нужны.
Имена вызываемых функций в Swift, с помощью которых можно преобразовать типы данных, соответствуют именам типов данных:
• Int(_:) — преобразовывает переданное значение к типу данных Int.
• Double(_:) — преобразовывает переданное значение к типу данных Double.
• Float(_:) — преобразовывает переданное значение к типу данных Float.
ПРИМЕЧАНИЕ Если вы используете типы данных вроде UInt, Int8 и т.д. в своей программе, то для преобразования чисел в эти типы данных также используйте функции, совпадающие по названиям с типами.
Для применения данных функций в скобках после названия требуется передать преобразуемый элемент (переменную, константу, число). Рассмотрим пример, в котором требуется перемножить два числа: целое типа Int и дробное типа Double (листинг 5.13).
Листинг 5.13
// переменная типа Int
var numberInt = 19
//переменная типа Double
var numberDouble = 3.13
// операция перемножения чисел
var resD = Double(numberInt) * numberDouble // 59,47
var resI = numberInt * Int(numberDouble) // 57
Существует два подхода к перемножению чисел в переменных numberInt и numberDouble:
• преобразовать значение переменной numberDouble в Int и перемножить два целых числа;
• преобразовать значение переменной numberInt в Double и перемножить два дробных числа.
По выводу в области результатов видно, что переменная resD имеет более точное значение, чем переменная resI. Это говорит о том, что вариант, преобразующий целое число в дробное с помощью функции Double(_:), точнее, чем использование функции Int(_:) для переменной типа Double, так как во втором случае дробная часть отбрасывается и игнорируется при расчетах.
ПРИМЕЧАНИЕ При преобразовании числа с плавающей точкой в целочисленный тип дробная часть отбрасывается, округление не производится.
Swift позволяет максимально сократить объем кода. И чем глубже вы будете постигать этот замечательный язык, тем больше приемов, способных облегчить жизнь, вы узнаете. Одним из них является совмещение оператора арифметической операции (+, -, *, /, %) и оператора присваивания (=). Рассмотрим пример, в котором создадим целочисленную переменную и с помощью составного оператора присваивания будем изменять ее значение, используя минимум кода (листинг 5.14).
Листинг 5.14
// переменная типа Int
var someNumInt = 19
// прибавим к ней произвольное число
someNumInt += 5 // 24
/* эта операция аналогична выражению
someNumInt = someNumInt+5 */
// умножим его на число 3
someNumInt *= 3 // 72
/* эта операция аналогична выражению
someNumInt = someNumInt*3 */
// вычтем из него число 3
someNumInt -= 3 // 69
/* эта операция аналогична выражению
someNumInt = someNumInt-3 */
// найдем остаток от деления на 8
someNumInt %= 8 // 5
/* эта операция аналогична выражению
someNumInt = someNumInt%8 */
Для использования составного оператора присваивания необходимо после оператора арифметической операции без пробелов написать оператор присваивания. Результат операции возвращается и записывается в параметр, находящийся слева от составного оператора.
Благодаря составным операторам присваивания вы знаете уже минимум два способа проведения арифметических операций, например увеличение значения на единицу (листинг 5.15).
Листинг 5.15
// переменная типа Int
var myNumInt = 19
// увеличим ее значение с использованием арифметической операции сложения
myNumInt = myNumInt + 1 // 20
// увеличим ее значение с использованием составного оператора присваивания
myNumInt += 1 // 21
Каждое выражение увеличивает переменную myNumInt ровно на единицу.
ПРИМЕЧАНИЕ Использование составного оператора является той заменой операторам инкремента и декремента, которую предлагает нам Apple.
Если в вашей школьной программе присутствовала информатика, то вы, возможно, знаете, что существуют различные системы счисления, например десятичная или двоичная. В реальном мире в большинстве случаев используется десятичная система, в то время как в компьютере все вычисления происходят в двоичной системе.
Swift при разработке программ позволяет задействовать самые популярные системы счисления:
• Двоичная. Числа записываются с использованием префикса 0b перед числом.
• Восьмеричная. Числа записываются с использованием префикса 0o перед числом.
• Шестнадцатеричная. Числа записываются с использованием префикса 0х перед числом.
• Десятичная. Числа записываются без использования префикса в привычном и понятном для нас виде.
Целые числа могут быть записаны в любой из приведенных систем счисления. В листинге 5.16 продемонстрирован пример записи числа 17 в различных системах.
Листинг 5.16
// 17 в десятичном виде
let decimalInteger = 17
// 17 в двоичном виде
let binaryInteger = 0b10001
// 17 в восьмеричном виде
let octalInteger = 0o21
// 17 в шестнадцатеричном виде
let hexadecimalInteger = 0x11
В области результатов отображается, что каждое из приведенных чисел — это 17.
Числа с плавающей точкой могут быть десятичными (без префикса) или шестнадцатеричными (с префиксом 0х). Такие числа должны иметь одинаковую форму записи (систему исчисления) по обе стороны от точки.
Помимо этого, Swift позволяет использовать экспоненту. Для этого применяется символ e для десятичных чисел и символ p для шестнадцатеричных.
Для десятичных чисел экспонента указывает на степень десятки:
• 1.25e2 соответствует 1,25 × 102, или 125,0.
Для шестнадцатеричных чисел экспонента указывает на степень двойки:
• 0xFp-2 соответствует 15 × 2–2, или 3,75.
В листинге 5.17 приведен пример записи десятичного числа 12,1875 в различных системах счисления и с использованием экспоненты.
Листинг 5.17
// десятичное число
let deciamlDouble = 12.1875 // 12,1875
// десятичное число с экспонентой
// соответствует выражению
// exponentDouble = 1.21875*101
let exponentDouble = 1.21875e1 // 12,1875
// шестнадцатеричное число с экспонентой
// соответствует
// выражению hexadecimalDouble = 0xC.3*20
let hexadecimalDouble = 0xC.3p0 // 12,1875
Арифметические операции доступны для чисел, записанных в любой из систем счисления. В области результатов вы всегда будете видеть результат выполнения в десятичном виде.
При записи числовых значений можно использовать символ нижнего подчеркивания (андерскор) для визуального отделения порядков числа (листинг 5.18).
Листинг 5.18
var number = 1_000_000 // 1000000
var nextNumber = 1000000 // 1000000
В обоих случаях, что с символом нижнего подчеркивания, что без него, получилось одно и то же число.
Андерскоры можно использовать для любого числового типа данных и для любой системы счисления.
Числа очень важны в программировании, но все же они не являются единственным видом данных, с которым вам предстоит взаимодействовать в ходе разработки приложений. Строковые типы данных также очень распространены. Они позволяют работать с текстом. Вы можете представить программу без текста? Это непростая задача!
В качестве примеров текстовых данных в ваших программах могут служить имена людей, адреса их проживания, их логины и многое другое.
В этом разделе вы получите первичное представление о том, что же такое строки и строковые типы данных, из чего они состоят и как могут быть использованы в Swift.
Строка в естественном языке — это набор отдельных символов. Любой текст в конечном итоге состоит из символов, на каком бы языке ни писали. Символы могут обозначать один звук, группу звуков, целое слово или даже предложение — это не важно. Важно то, что минимальная единица в естественном языке — это символ.
В разделе про принципы работы компьютера было сказано, что современные устройства представляют всю информацию в виде чисел, состоящих из цифр 0 и 1. Но отдельные символы текстовых данных и числа из 0 и 1 — это совершенно разные категории информации. Получается, что должны существовать механизмы, используя которые возможно представить любые текстовые данные в виде последовательности цифр.
И такой механизм существует. Имя ему Юникод (Unicode).
Unicode — это международный стандарт, описывающий, каким образом строки преобразуются в числа. До принятия Юникода было множество других стандартов, но главной их проблемой было то, что они в силу своей ограниченности не могли стать международными. Наверняка многие из вас помнят те времена, когда, заходя на сайт в интернете или открывая документ вы видели устрашающие кракозябры и совершенно нечитаемый текст. С того времени, как произошел переход Всемирной паутины на Юникод, такая проблема отпала.
Главная особенность Юникода в том, что для любого существующего символа (практически всех естественных языков) есть однозначно определяющая последовательность чисел. То есть для любого символа существует уникальная кодовая последовательность, называемая кодовой точкой (code point). Так, к примеру, маленькая латинская «a» имеет кодовую точку 97 (в десятичной системе счисления) или 61 (в шестнадцатеричной системе счисления).
ПРИМЕЧАНИЕ Если вы не ориентируетесь в существующих системах счисления, то советую познакомиться с ними самостоятельно с помощью дополнительного материала, например статей из Википедии.
Отмечу, что чаще всего вы будете использовать десятичную систему, а также шестнадцатеричную и двоичную.
Юникод содержит кодовые точки для огромного количества символов, включая латиницу, кириллицу, буквы других языков, знаки математических операций, иероглифы и даже эмодзи! Благодаря Юникоду данные, передаваемые между компьютерами, будут корректно раскодированы, то есть переведены из кодовой точки в символ.
Всего в описываемом стандарте есть место для более чем 1 миллиона символов, но на данный момент он содержит лишь около 140 тысяч (плюс некоторые диапазоны отданы для частного использования большим корпорациям). Оставшиеся места зарезервированы для будущего использования.
Приведем пример кодирования (перевода в числовую последовательность) слова «SWIFT» (заглавные буквы, латинский алфавит, кодовые точки в шестнадцатеричной системе счисления) (табл. 5.2).
Таблица 5.2. Соответствие символов и их кодовых точек
S | W | I | F | T |
53 | 57 | 49 | 46 | 54 |
Все представленные коды довольно просты, так как это латинский алфавит (он располагается в самом начале диапазона). Кодовые точки некоторых менее распространенных символов содержат по пять шестнадцатеричных цифр (например, 100A1).
Подобным образом может быть закодирован любой набор символов, для каждого из них найдется уникальный числовой идентификатор.
Но хотя мы и перевели слова в шестнадцатеричные числа, на аппаратном уровне должны быть лишь 0 и 1. Поэтому получившуюся числовую последовательность необходимо повторно закодировать. Для этой цели в состав стандарта Unicode входит не только словарь соответствий «символ — кодовая точка», но и описания специальных механизмов, называемых кодировками, к которым относятся UTF-8, UTF-16 и UTF-32. Кодировки определяют, каким образом из 5357494654 может получиться 0011010100111001001100010010111000110110, а эту последовательность без каких-либо проблем можно передать по каналам связи или обработать на аппаратном уровне.
Таким образом, Юникод содержит исчерпывающую информацию о том, как из текста получить последовательность битов.
Swift имеет полную совместимость со стандартом Unicode, поэтому вы можете использовать совершенно любые символы в работе. Подробному изучению работы со строковыми данными будет посвящен этот раздел и одна из будущих глав книги. Отнеситесь к данному материалу со всей внимательностью, так как он будет очень востребован для работы над вашими приложениями.
Немного отойдем от стандарта Unicode и займемся непосредственно строковыми данными в Swift.
Для работы с текстом предназначены два основным типа данных:
• тип Character предназначен для хранения отдельных символов;
• тип String предназначен для хранения произвольной текстовой информации. С ним мы уже встречались ранее.
Благодаря строковым типам данных вам обеспечена быстрая и корректная работа с текстом в программе.
Для создания строковых данных используются строковые литералы.
Строковый литерал — это фиксированная последовательность текстовых символов, окруженная с обеих сторон двойными кавычками ("").
Тип данных Character позволяет хранить строковый литерал длиной в один символ. Всего один символ! И не символом больше! Пример использования Character приведен в листинге 5.19.
Листинг 5.19
var char: Character = "a"
print(char)
Консоль
a
В переменной char хранится только один символ a.
При попытке передать строковый литерал длиной более одного символа в параметр типа Character Xcode сообщит об ошибке несоответствия типов записываемого значения и переменной, так как рассматривает его в качестве значения типа данных String (неявное определение типа данных).
С помощью типа данных String в параметрах могут храниться строки произвольной длины.
ПРИМЕЧАНИЕ Swift — все еще идет по пути становления, в каждой новой версии в нем происходят значительные изменения. Так, в Swift уже трижды менялся подход к работе со строковыми типами данных.
Для определения данного строкового типа необходимо использовать ключевое слово String. Пример приведен в листинге 5.20.
Листинг 5.20
// переменная типа String
// тип данных задается явно
var stringOne: String = "Dragon"
При объявлении переменной stringOne указывается ее тип данных, после чего передается строковый литерал. В качестве строкового литерала может быть передана строка с любым количеством символов.
В большинстве случаев не требуется указывать тип объявляемого строкового параметра, так как Swift неявно задает его при передаче строки любого размера (листинг 5.21).
Листинг 5.21
let language = "Swift" // тип данных - String
var version = "5" // тип данных — String
Обратите внимание, что в обоих случаях тип объявленного параметра String, и задан он неявно. В случае с переменной version переданное значение является строковым, а не числовым литералом, так как передано в кавычках.
Запомните, что для строковых значений необходим литерал в один символ, в этом случае неявно определяется тип данных String, но не Character.
Пустой строковый литерал — это строковый литерал, не содержащий символов. Другими словами, это пустая строка (кавычки без содержимого). Она также может быть проинициализирована в качестве значения.
Пустая строка (кавычки без содержимого) также является строковым литералом. Вы можете передать ее в качестве значения параметру типа данных String (листинг 5.22).
Листинг 5.22
// с помощью пустого строкового литерала
var emptyString = ""
// с помощью инициализатора типа String
var anotherEmptyString = String()
Обе строки в результате будут иметь идентичное (пустое) значение.
Напомню, что инициализатор — это специальный метод, встроенный в тип данных, в данном случае в String, который позволяет создать хранилище нужного нам типа.
При попытке инициализировать пустой строковый литерал параметру типа Character Xcode сообщит об ошибке несоответсвия типов, так как пустая строка не является отдельным символом (рис. 5.1). Она является пустой строкой.
Рис. 5.1. Попытка инициализировать пустой строковый литерал
Swift позволяет писать строковые литералы в несколько строк, разделяя их символом переноса (нажатием клавиши Enter). В этом случае текст обрамляется с обеих сторон тремя двойными кавычками. При этом открывающие и закрывающие тройки кавычек обязательно должны находиться на строке, не содержащей текст литерала:
"""
строковый_литерал
"""
Пример приведен в листинге 5.23.
Листинг 5.23
let longString = """
Это многострочный
строковый литерал
"""
Как уже неоднократно говорилось, помимо непосредственной передачи литерала, вы можете использовать специальную функцию, в данном случае String(_:), для инициализации значения строкового типа (5.24).
Листинг 5.24
// инициализация строкового значения
var notEmptyString = String("Hello, Troll!")
В переменной notEmptyString содержится строковое значение "Hello, Troll!".
С помощью данной функции (по аналогии с числовыми типами данных) можно привести значение другого типа данных к строковому. К примеру, преобразуем дробное число к типу данных String (листинг 5.25).
Листинг 5.25
// переменная типа Double
var numDouble = 74.22
// строка, созданная на основе переменной типа Double
var numString = String(numDouble) // "74.22"
При необходимости вы можете объединять несколько строк в одну более длинную. Для этого существует два механизма: интерполяция и конкатенация.
При интерполяции происходит объединение строковых литералов, переменных, констант и выражений в едином строковом литерале (листинг 5.26).
Листинг 5.26
// переменная типа String
var name = "Дракон"
// константа типа String c использованием интерполяции
let hello = "Привет, меня зовут \(name)!"
// интерполяция с использованием выражения
var meters: Double = 10
let text = "Моя длина \(meters * 3.28) фута"
// выведем значения на консоль
print(hello)
print(text)
Консоль
Привет, меня зовут Дракон!
Моя длина 32.8 фута
При инициализации значения константы hello используется переменная name. Такой подход мы видели ранее при знакомстве с функцей print(_:).
Самое интересное, что, помимо имен параметров, вы можете использовать любое выражение (например, арифметическую операцию умножения), что и продемонстрировано в предыдущем листинге.
При конкатенации происходит объединение различных строковых значений в одно с помощью оператора сложения (+) (листинг 5.27).
Листинг 5.27
// константа типа String
let firstText = "Мой вес "
// переменная типа Double
var weight = 12.4
// константа типа String
let secondText = " тонны"
// конкатенация строк при инициализации значения новой переменной
var resultText = firstText + String(weight) + secondText
print(resultText)
Консоль
Мой вес 12.4 тонны
В данном примере используется оператор сложения для объединения различных строковых значений. Тип данных переменной weight не строковый, поэтому ее значение приводится к String с помощью соответствующей функции.
ПРИМЕЧАНИЕ Значения типа Character при конкатенации также должны преобразовываться к типу String.
Swift позволяет производить сравнение двух различных строк. Для этой цели используется оператор сравнения (==). Рассмотрим пример в листинге 5.28.
Листинг 5.28
let firstString = "Swift"
let secondString = "Objective-C"
let anotherString = "Swift"
firstString == secondString // false
firstString == anotherString // true
Значения, отображаемые в области результатов (false, true), определяют результат сравнения строк: false соответствует отрицательному результату, а true — положительному.
ПРИМЕЧАНИЕ Данная операция сравнения называется логической операцией. Она может быть проведена в том числе и для числовых значений. Подробнее с ней мы познакомимся уже в следующем разделе.
В строковых литералах для определения символов можно использовать так называемые юникод-скаляры — специальные конструкции, состоящие из набора символов \u{} и заключенной между фигурными скобками кодовой точки символа в шестнадцатеричной форме.
В листинге 5.29а приведен пример использования юникод-скаляра для инициализации кириллического символа К в качестве значения параметра типа Character.
Листинг 5.29а
var myCharOverUnicode: Character = "\u{041A}"
myCharOverUnicode // К
Но не только тип Character совместим с Unicode, вы также можете использовать скаляры и для значений типа String (листинг 5.29б).
Листинг 5.29б
var stringOverUnicode = "\u{41C}\u{438}\u{440}\u{20}\u{412}\u{430}\u{43C}\u{21}"
stringOverUnicode // "Мир Вам!"
Для каждого символа используется собственный юникод-скаляр (в том числе и для пробела). Но вы можете комбинировать, определяя в строке, таким образом, только необходимые символы, а остальные оставляя как есть.
В будущем мы вернемся к Unicode, более подробно изучив принципы его работы и методы взаимодействия со строковыми данными.
Изучение фундаментальных типов данных не завершается на числовых и строковых. В Swift существует специальный логический тип данных, называемый Bool, способный хранить одно из двух значений: «истина» или «ложь». Значение «истина» обозначается как true, а «ложь» — как false. Вспомните, мы видели их, когда сравнивали строки.
Объявим переменную и константу логического типа данных (листинг 5.30).
Листинг 5.30
// логическая переменная с неявно заданным типом
var isDragon = true
// логическая константа с явно заданным типом
let isKnight: Bool = false
Как и для других типов данных в Swift, для Bool возможно явное и неявное определение типа, что видно из приведенного примера.
ПРИМЕЧАНИЕ Строгая типизация Swift препятствует замене других типов данных на Bool, как вы могли видеть в других языках, где, например, строки i = 1 и i = true обозначали одно и то же. В Xcode подобный подход вызовет ошибку.
Тип данных Bool обычно используется при работе с оператором if—else (познакомимся с ним несколько позже), который в зависимости от значения переданного ему параметра позволяет пускать выполнение программы по различным ветвям (листинг 5. 31).
Листинг 5.31
// логическая переменная
var isDragon = true
// конструкция условия
if isDragon {
print("Привет, Дракон!")
}else{
print("Привет, Тролль!")
}
Консоль:
Привет, Дракон!
Как вы можете видеть, на консоль выводится фраза «Привет, Дракон!». Оператор условия if-else проверяет, является ли переданное ему выражение истинным, и в зависимости от результата выполняет соответствующую ветвь.
Если бы переменная isDragon содержала значение false, то на консоль была бы выведена фраза «Привет, Тролль!».
Логические операторы проверяют истинность какого-либо утверждения и возвращают соответствующее логическое значение. Swift поддерживает три стандартных логических оператора:
• логическое НЕ (!a);
• логическое И (a && b);
• логическое ИЛИ (a || b).
Унарный оператор логического НЕ является префиксным и записывается символом восклицания. Он возвращает инвертированное логическое значение операнда, то есть если операнд имел значение true, то вернется false, и наоборот. Для выражения !a данный оператор может быть прочитан как «не a» (листинг 5.32)
Листинг 5.32
var someBool = true
// инвертируем значение
!someBool // false
someBool // true
Переменная someBool изначально имеет логическое значение true. С помощью оператора логического НЕ возвращается инвертированное значение переменной someBool. При этом значение в самой переменной не меняется.
Бинарный оператор логического И записывается в виде удвоенного символа «амперсанд» и является инфиксным. Он возвращает true, когда оба операнда имеют значение true. Если значение хотя бы одного из операндов равно false, то возвращается значение false (листинг 5.33).
Листинг 5.33
let firstBool = true, secondBool = true, thirdBool = false
// группируем различные условия
var one = firstBool && secondBool // true
var two = firstBool && thirdBool // false
var three = firstBool && secondBool && thirdBool // false
Оператор логического И позволяет определить, есть ли среди переданных ему операндов ложные значения.
Бинарный оператор логического ИЛИ выглядит как удвоенный символ прямой черты и является инфиксным. Он возвращает true, когда хотя бы один из операндов имеет значение true. Если значения обоих операндов равны false, то возвращается значение false (листинг 5.34).
Листинг 5.34
let firstBool = true, secondBool = false, thirdBool = false
// группируем различные условия
let one = firstBool || secondBool // true
let two = firstBool || thirdBool // true
let three = secondBool || thirdBool // false
Оператор логического ИЛИ позволяет определить, есть ли среди значений переданных ему операндов хотя бы одно истинное.
Различные логические операторы можно группировать между собой, создавая сложные логические структуры. Пример показан в листинге 5.35.
Листинг 5.35
let firstBool = true, secondBool= false, thirdBool = false
var resultBool = firstBool && secondBool || thirdBool // false
var resultAnotherBool = thirdBool || firstBool && firstBool // true
При вычислении результата выражения Swift определяет значение подвыражений последовательно, то есть сначала первого, потом второго и т.д.
Для того чтобы указать порядок вычисления операций, необходимо использовать круглые скобки (точно как в математических примерах). То, что указано в скобках, будет выполняться в первую очередь (листинг 5.36).
Листинг 5.36
let firstBool = true, secondBool= false, thirdBool = true
var resultBool = firstBool && (secondBool || thirdBool) // true
var resultAnotherBool = (secondBool || (firstBool && thirdBool)) && thirdBool // true
Swift позволяет производить сравнение однотипных значений друг с другом. Для этой цели используются операторы сравнения, результатом работы которых является значение типа Bool. Всего существует шесть стандартных операторов сравнения:
== Бинарный оператор эквивалентности (a == b) возвращает true, когда значения обоих операндов эквивалентны.
!= Бинарный оператор неэквивалентности (a != b) возвращает true, когда значения операндов различны.
> Бинарный оператор «больше чем» (a > b) возвращает true, когда значение первого операнда больше значения второго операнда.
< Бинарный оператор «меньше чем» (a < b) возвращает true, когда значение первого операнда меньше значения второго операнда.
>= Бинарный оператор «больше или равно» (a >= b) возвращает true, когда значение первого операнда больше значения второго операнда или равно ему.
<= Бинарный оператор «меньше или равно» (a <= b) возвращает true, когда значение первого операнда меньше значения второго операнда или равно ему.
Каждый из приведенных операторов возвращает значение, указывающее на справедливость некоторого утверждения. Несколько примеров и значений, которые они возвращают, приведены в листинге 5.37.
Листинг 5.37
// Утверждение "1 больше 2"
1 > 2 // false
// вернет false, так как оно ложно
// Утверждение "2 не равно 2"
2 != 2 // false
// вернет false, так как оно ложно
// Утверждение "1 плюс 1 меньше 3"
(1+1) < 3 // true
// вернет true, так как оно истинно
// Утверждение "5 больше или равно 1"
5 >= 1 // true
// вернет true, так как оно истинно
Оператор сравнения можно использовать, например, с упомянутой выше конструкцией if-else. В следующих главах вы будете часто прибегать к его возможностям.
Swift предоставляет возможность создания псевдонима для любого типа данных. Псевдонимом типа называется дополнительное имя, по которому будет происходить обращение к данному типу. Для создания псевдонима используется оператор typealias. Псевдоним необходимо применять тогда, когда существующее имя типа неудобно использовать в контексте программы (листинг 5.38).
Листинг 5.38
// определяем псевдоним для типа UInt8
typealias ageType = UInt8
/* создаем переменную типа UInt8,
используя псевдоним */
var myAge: ageType = 29
В результате будет создана переменная myAge, имеющая значения типа UInt8.
ПРИМЕЧАНИЕ При разработке программ вы будете встречаться со сложными типами данных, для которых применение оператора typealias значительно улучшает читабельность кода.
У типа может быть произвольное количество псевдонимов. И все псевдонимы вместе с оригинальным названием типа можно использовать в программе (листинг 5.39).
Листинг 5.39
// определяем псевдоним для типа String
typealias textType = String
typealias wordType = String
typealias charType = String
//создаем переменные каждого типа
var someText: textType = "Это текст"
var someWord: wordType = "Слово"
var someChar: charType = "Б"
var someString: String = "Строка типа String"
В данном примере для типа данных String определяется три различных псевдонима. Каждый из них наравне с основным типом может быть использован для объявления параметров.
Созданный псевдоним наделяет параметры теми же возможностями, что и родительский тип данных. Однажды объявив его, вы сможете использовать данный псевдоним для доступа к свойствам и методам типа (листинг 5.40).
Листинг 5.40
// объявляем псевдоним
typealias ageType = UInt8
/* используем свойство типа
UInt8 через его псевдоним */
var maxAge = ageType.max //255
Для Swift обращение к псевдониму равносильно обращению к самому типу данных. Псевдоним — это ссылка на тип. В данном примере используется псевдоним maxAge для доступа к типу данных UInt8 и свойству max.
ПРИМЕЧАНИЕ Запомните, что псевдонимы можно использовать для совершенно любых типов. И если данные примеры недостаточно полно раскрывают необходимость использования оператора typealias, то при изучении кортежей (в следующих разделах) вы встретитесь с составными типами, содержащими два и более подтипа. С такими составными типами также можно работать через псевдонимы.
Осталось лишь несколько вопросов, которые необходимо рассмотреть, прежде чем двигаться к более сложным и не менее интересным темам.
Xcode и Swift позволяют узнать тип данных любого объявленного параметра. Для этого могут быть использованы два способа: либо с помощью справочного окна, либо с помощью глобальной функции type(of:).
Если на клавиатуре зажать клавишу Option и навести указатель мыши на произвольный параметр в редакторе кода, то он должен принять вид знака вопроса. После щелчка левой кнопкой мыши появится всплывающее окно, содержащее справочную информацию, в которой и будет указан его тип данных (рис. 5.2).
Рис. 5.2. Уточнение типа данных параметра
Обратите внимание, что при объявлении параметра его тип данных был указан неявно, но во всплывающем окне он прописан. Кстати, данный способ получения справочной информации можно использовать не только с параметрами, но и с любыми другими элементами языка.
Также тип данных может быть определен с помощью глобальной функции type(of:). В качестве входного аргумента необходимо передать имя параметра, тип которого необходимо определить. По возвращенному этой функцией значению можно сделать вывод о типе данных. Пример использования type(of:) показан в листинге 5.41.
Листинг 5.41
var myVar = 3.54
print( type(of: myVar) )
Консоль
Double
В будущих листингах данная функция будет применяться довольно часто для того, чтобы вы понимали, с данными каких типов работаете.
ПРИМЕЧАНИЕ Если использовать функцию type(of:) без вывода на консоль, то в области результатов к имени класса будет добавлено «.Type», например Double.Type. Для идентификации типа просто отбросьте данное окончание.
Типы данных могут быть классифицированы и сгруппированы по самым разным характеристикам, и чем дальше вы будете изучать материал, тем больше таких характеристик узнаете. Если тип обеспечивает какой-то определенный функционал, то он может быть отнесен к характерной категории, в которую помимо него могут входить и другие типы. Одними из важнейших категорий, с которыми вы будете встречаться в книге, являются хешируемые и сопоставимые типы данных.
Хешируемым (Hashable) называется такой тип данных, для значения которого может быть высчитан специальный цифровой код (называемый хешем). Причем этот код должен быть уникальным для любого значения данного типа.
ПРИМЕЧАНИЕ Хеш — это понятие из области криптографии. Он может принимать не только цифровой, но и буквенно-цифровой вид. Для его получения используются хеш-функции, реализующие алгоритмы шифрования. Их рассмотрение выходит за рамки данной книги, но я настоятельно советую ознакомиться с данным материалом самостоятельно.
Если тип данных является хешируемым, то его значение имеет свойство hashValue, к которому вы можете обратиться (листинг 5.42).
Листинг 5.42
var stringForHash = "Строка текста"
var intForHash = 23
var boolForHash = false
stringForHash.hashValue // 109231433150392402
intForHash.hashValue // 5900170382727681744
boolForHash.hashValue // 820153108557431465
Значения, возвращаемые свойством hashValue в вашем случае, будут отличаться (а также будут изменяться при каждом новом исполнении кода). Это связано с тем, что для высчитывания хеша используются переменные параметры вроде текущего времени.
Все изученные вами фундаментальные типы являются хешируемыми.
Сопоставимым (Comparable) называется такой тип данных, значения которого могут быть сопоставлены между собой с помощью операторов логического сравнения <, <=, >= и >. Другими словами, значения этого типа можно сравнить, чтобы определить, какое из них больше, а какое меньше.
Определить, является ли тип данных сопоставимым, очень просто. Достаточно, используя один из перечисленных логических операторов, сравнить два значения этого типа. Если в результате этого выражения будет возвращено true или false, то такой тип называется сопоставимым. Все строковые и числовые типы являются сопоставимыми, а вот Bool не позволяет сравнивать свои значения.
ПРИМЕЧАНИЕ Обратите внимание, что для сравнения используются логические операторы <, <=, >= и >. Операторы эквивалентности == и неэквивалентности != отсутствует в этом списке. Если тип данных позволяет использовать == и != для сравнения значений, то он относится к категории эквивалентных (Equatable).
Как вы можете видеть, в Swift просто огромное количество различных категорий, по которым могут быть разделены типы данных.
В листинге 5.43 показан пример проверки типов String и Bool на предмет возможности сопоставления значений.
Листинг 5.43
"string1" < "string2" //true
true < false // error: Binary operator '<' cannot be applied to two 'Bool' operands
Как видно из результата, тип данных String является сопоставимым, а Bool — нет (выражение вызвало ошибку).
Вы стали еще на один шаг ближе к написанию великолепных программ. Понять, что такое типы данных, и начать их использовать — это самый важный шаг в изучении Swift. Важно, чтобы также вы запомнили такие понятия, как хешируемый (Hashable) и сопоставимый (Comparable), так как уже в скором времени мы вернемся к ним для более подробного изучения материала.
ПРИМЕЧАНИЕ Помните, что все задания для самостоятельного решения представлены на сайте swiftme.ru.