Spec-Zone.ru › Swift Language

Основы

Работайте с распространёнными типами данных и записывайте базовый синтаксис.

Swift предоставляет множество основных типов данных, включая Int для целых чисел, Double для чисел с плавающей запятой, Bool для логических значений и String для текста. Swift также предоставляет мощные версии трёх основных типов коллекций, Array, Set и Dictionary, как описано в CollectionTypes.

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

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

Swift также вводит необязательные типы, которые обрабатывают отсутствие значения. Необязательные типы говорят либо «есть значение, и оно равно x», либо «значения нет вообще».

Swift — безопасный язык, что означает, что язык помогает вам чётко понимать типы значений, с которыми может работать ваш код. Если часть вашего кода требует String, безопасность типов предотвращает случайную передачу ему Int. Аналогично, безопасность типов предотвращает случайную передачу необязательного String в часть кода, которая требует не-необязательного String. Безопасность типов помогает обнаруживать и исправлять ошибки как можно раньше на стадии разработки.

Константы и переменные

Константы и переменные связывают имя (например, maximumNumberOfLoginAttempts или welcomeMessage) со значением определённого типа (например, число 10 или строка "Hello"). Значение константы нельзя изменить после её установки, тогда как переменная может быть установлена в другое значение в будущем.

Объявление констант и переменных

Константы и переменные должны быть объявлены до использования. Константы объявляются с ключевым словом let, а переменные — с ключевым словом var. Вот пример того, как константы и переменные могут использоваться для отслеживания количества попыток входа в систему, сделанных пользователем:

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

Этот код можно прочитать как:

«Объявить новую константу под названием maximumNumberOfLoginAttempts и присвоить ей значение 10. Затем объявить новую переменную под названием currentLoginAttempt и присвоить ей начальное значение 0».

В этом примере максимальное количество разрешённых попыток входа в систему объявлено как константа, потому что максимальное значение никогда не меняется. Текущий счётчик попыток входа в систему объявлен как переменная, потому что это значение должно увеличиваться после каждой неудачной попытки входа в систему.

Если хранимое значение в вашем коде не изменится, всегда объявляйте его как константу с ключевым словом let. Используйте переменные только для хранения значений, которые меняются.

При объявлении константы или переменной вы можете присвоить ей значение в рамках этого объявления, как в приведённых выше примерах. Кроме того, вы можете присвоить её начальное значение позже в программе, при условии, что оно гарантированно будет иметь значение до первого чтения из неё.

var environment = "development"
let maximumNumberOfLoginAttempts: Int
// maximumNumberOfLoginAttempts has no value yet.

if environment == "development" {
    maximumNumberOfLoginAttempts = 100
} else {
    maximumNumberOfLoginAttempts = 10
}
// Now maximumNumberOfLoginAttempts has a value, and can be read.

В этом примере максимальное количество попыток входа в систему — константа, и её значение зависит от среды. В среде разработки оно равно 100; в любой другой среде — 10. Обе ветви оператора if инициализируют maximumNumberOfLoginAttempts каким-то значением, гарантируя, что константа всегда получает значение. Сведения о том, как Swift проверяет ваш код при установке начального значения таким образом, см. в <doc:Declarations#Constant-Declaration>.

Вы можете объявлять несколько констант или несколько переменных в одной строке, разделяя их запятыми:

var x = 0.0, y = 0.0, z = 0.0

Аннотации типов

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

В этом примере для переменной welcomeMessage указана аннотация типа, чтобы указать, что переменная может хранить значения String:

var welcomeMessage: String

Двоеточие в объявлении означает «…типа…», поэтому приведенный выше код можно прочитать как:

«Объявить переменную под названием welcomeMessage, которая является типа String».

Фраза «типа String» означает «может хранить любое значение String». Подумайте об этом как о значении «вещь типа» (или «вид вещи»), которая может быть сохранена.

Теперь переменная welcomeMessage может быть установлена в любое строковое значение без ошибок:

welcomeMessage = "Hello"

Вы можете определять несколько связанных переменных одного типа в одной строке, разделяя их запятыми, с одной аннотацией типа после имени последней переменной:

var red, green, blue: Double

Примечание: на практике редко требуется писать аннотации типов. Если вы предоставляете начальное значение для константы или переменной в момент её определения, Swift практически всегда может определить тип, который следует использовать для этой константы или переменной, как описано в <doc:TheBasics#Type-Safety-and-Type-Inference>. В примере welcomeMessage выше не предоставляется начальное значение, поэтому тип переменной welcomeMessage задаётся с помощью аннотации типа, а не определяется из начального значения.

Именование констант и переменных

Имена констант и переменных могут содержать практически любые символы, включая символы Юникода:

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

Имена констант и переменных не могут содержать пробелов, математических символов, стрелок, символов Юникода для частного использования, символов черчения линий и рамок. Они также не могут начинаться с цифры, хотя цифры могут быть включены в другое место в имени.

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

Примечание: если вам нужно присвоить константе или переменной то же имя, что и зарезервированному ключевому слову Swift, заключите ключевое слово в обратные кавычки (`), когда вы его используете как имя. Однако избегайте использования ключевых слов в качестве имён, если это абсолютно необходимо.

Вы можете изменить значение существующей переменной на другое значение совместимого типа. В этом примере значение friendlyWelcome изменяется с "Hello!" на "Bonjour!":

var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"

В отличие от переменной, значение константы после её установки изменить нельзя. Попытка сделать это будет сообщена как ошибка при компиляции вашего кода:

let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.

Вывод констант и переменных

Вы можете вывести текущее значение константы или переменной с помощью функции print(_:separator:terminator:):

print(friendlyWelcome)
// Prints "Bonjour!"

Функция print(_:separator:terminator:) — это глобальная функция, которая выводит одно или несколько значений в соответствующий выходной поток. В Xcode, например, функция print(_:separator:terminator:) выводит свой вывод в области «консоль» Xcode. Параметры separator и terminator имеют значения по умолчанию, поэтому вы можете опустить их при вызове этой функции. По умолчанию функция завершает вывод строки, добавляя символ перевода строки. Чтобы вывести значение без символа перевода строки после него, передайте пустую строку в качестве разделителя — например, print(someValue, terminator: ""). Сведения о параметрах с значениями по умолчанию см. в <doc:Functions#Default-Parameter-Values>.

Swift использует встраивание строк для включения имени константы или переменной в качестве заполнителя в более длинной строке и для указания Swift на замену его текущим значением этой константы или переменной. Оберните имя в круглые скобки и экранируйте его обратной косой чертой перед открывающей скобкой:

print("The current value of friendlyWelcome is \(friendlyWelcome)")
// Prints "The current value of friendlyWelcome is Bonjour!"

Примечание: все варианты, которые можно использовать с интерполяцией строк, описаны в <doc:StringsAndCharacters#String-Interpolation>.

Комментарии

Используйте комментарии, чтобы включить в свой код невыполняемый текст в качестве примечания или напоминания себе. Компилятор Swift игнорирует комментарии при компиляции вашего кода.

Комментарии в Swift очень похожи на комментарии в C. Однострочные комментарии начинаются с двух слэшей (//):

// This is a comment.

Многострочные комментарии начинаются с слэша, за которым следует звездочка (/*), и заканчиваются звездочкой, за которой следует слэш (*/):

/* This is also a comment
but is written over multiple lines. */

В отличие от многострочных комментариев в C, многострочные комментарии в Swift могут быть вложены в другие многострочные комментарии. Вложенные комментарии пишутся путём начала блока многострочного комментария и затем начала второго многострочного комментария внутри первого блока. Затем второй блок закрывается, за которым следует первый блок:

/* This is the start of the first multiline comment.
    /* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */

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

Точки с запятой

В отличие от многих других языков, Swift не требует писать точку с запятой (;) после каждой инструкции в вашем коде, хотя вы можете это сделать, если хотите. Однако точки с запятой требуются, если вы хотите записать несколько отдельных инструкций в одной строке:

let cat = "🐱"; print(cat)
// Prints "🐱"

Целые числа

Целые числа — это целые числа без дробной части, такие как 42 и -23. Целые числа бывают знаковыми (положительными, нулевыми или отрицательными) или беззнаковыми (положительными или нулевыми).

Swift предоставляет знакомые и беззнаковые целые числа в 8-, 16-, 32- и 64-разрядных форматах. Эти целые числа следуют соглашению об именах, аналогичному C, в том смысле, что 8-битное беззнаковое целое число имеет тип UInt8, а 32-битное знаковое целое число имеет тип Int32. Как и все типы в Swift, эти типы целых чисел имеют имена с заглавными буквами.

Пределы целых чисел

Вы можете получить минимальные и максимальные значения каждого типа целых чисел с помощью свойств min и max:

let minValue = UInt8.min  // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max  // maxValue is equal to 255, and is of type UInt8

Значения этих свойств имеют тип соответствующего размера (например, UInt8 в приведённом выше примере) и, следовательно, могут использоваться в выражениях вместе с другими значениями того же типа.

Int

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

  • На 32-битной платформе Int имеет тот же размер, что и Int32.
  • На 64-битной платформе Int имеет тот же размер, что и Int64.

Если вам не нужно работать со значениями целого типа определенного размера, всегда используйте Int для целых значений в вашем коде. Это способствует согласованности кода и межплатформенной совместимости. Даже на 32-битных платформах Int может хранить любое значение между -2,147,483,648 и 2,147,483,647 и достаточно велико для многих диапазонов целых чисел.

UInt

Swift также предоставляет тип беззнакового целого числа UInt, размер которого соответствует размеру родного слова текущей платформы:

  • На 32-битной платформе UInt имеет тот же размер, что и UInt32.
  • На 64-битной платформе UInt имеет тот же размер, что и UInt64.

Примечание: Используйте UInt только в тех случаях, когда вам нужен тип беззнакового целого числа с размером, соответствующим размеру родного слова платформы. Если это не так, предпочтительнее использовать Int, даже если значения для хранения известны как неотрицательные. Согласованное использование Int для целых значений способствует межплатформенной совместимости кода, избегает необходимости преобразования между различными типами чисел и соответствует выводу типов целых чисел, как описано в <doc:TheBasics#Type-Safety-and-Type-Inference>.

Числа с плавающей точкой

Числа с плавающей точкой — это числа с дробной частью, такие как 3.14159, 0.1 и -273.15.

Типы чисел с плавающей точкой могут представлять гораздо более широкий диапазон значений, чем целые типы, и могут хранить числа, которые значительно больше или меньше, чем могут храниться в Int. Swift предоставляет два типа чисел со знаком с плавающей точкой:

  • Double представляет 64-битное число с плавающей точкой.
  • Float представляет 32-битное число с плавающей точкой.

Примечание: Double имеет точность не менее 15 десятичных знаков, в то время как точность Float может составлять всего 6 десятичных знаков. Тип числа с плавающей точкой, который следует использовать, зависит от характера и диапазона значений, с которыми вы работаете в вашем коде. В ситуациях, когда подходит любой из типов, предпочтительнее использовать Double.

Безопасность типов и вывод типов

Swift — это типобезопасный язык. Типобезопасный язык побуждает вас четко определять типы значений, с которыми может работать ваш код. Если какая-то часть вашего кода требует String, вы не сможете случайно передать ей Int.

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

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

Благодаря выводу типов Swift требует гораздо меньше объявлений типов, чем языки вроде C или Objective-C. Константы и переменные по-прежнему явно типизированы, но большая часть работы по указанию их типа выполняется за вас.

Вывод типов особенно полезен при объявлении константы или переменной с начальным значением. Это часто делается путем присвоения литерального значения (или литерала) константе или переменной в момент ее объявления. (Литеральное значение — это значение, которое появляется непосредственно в вашем исходном коде, например, 42 и 3.14159 в примерах ниже.)

Например, если вы присваиваете литеральное значение 42 новой константе, не указывая ее тип, Swift определит, что константа должна быть Int, так как вы инициализировали ее числом, выглядящим как целое число:

let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int

Точно так же, если вы не указываете тип для литерала с плавающей точкой, Swift определит, что вы хотите создать Double:

let pi = 3.14159
// pi is inferred to be of type Double

Swift всегда выбирает Double (а не Float) при выводе типа чисел с плавающей точкой.

Если вы комбинируете целые и вещественные литералы в выражении, из контекста будет выведен тип Double:

let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double

Литеральное значение 3 само по себе не имеет явного типа, поэтому соответствующий выходной тип Double выводится из наличия числа с плавающей точкой как части сложения.

Числовые литералы

Целые литералы могут быть записаны как:

  • Десятичное число без префикса
  • Двоичное число с префиксом 0b
  • Восьмеричное число с префиксом 0o
  • Шестнадцатеричное число с префиксом 0x

Все эти целые литералы имеют десятичное значение 17:

let decimalInteger = 17
let binaryInteger = 0b10001       // 17 in binary notation
let octalInteger = 0o21           // 17 in octal notation
let hexadecimalInteger = 0x11     // 17 in hexadecimal notation

Литералы чисел с плавающей точкой могут быть десятичными (без префикса) или шестнадцатеричными (с префиксом 0x). Они всегда должны иметь число (или шестнадцатеричное число) по обе стороны десятичной точки. Десятичные числа с плавающей точкой также могут иметь необязательный экспоненциальный показатель, обозначаемый прописной или строчной буквой e; шестнадцатеричные числа с плавающей точкой должны иметь показатель степени, обозначаемый прописной или строчной буквой p.

Для десятичных чисел с показателем x, основание умножается на 10ˣ:

  • 1.25e2 означает 1,25 x 10², или 125.0.
  • 1.25e-2 означает 1,25 x 10⁻², или 0.0125.

Для шестнадцатеричных чисел с показателем x, основание умножается на 2ˣ:

  • 0xFp2 означает 15 x 2², или 60.0.
  • 0xFp-2 означает 15 x 2⁻², или 3.75.

Все эти литералы чисел с плавающей точкой имеют десятичное значение 12.1875:

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0

Числовые литералы могут содержать дополнительную форматирование для лучшей читабельности. Как целые, так и вещественные числа могут быть дополнены дополнительными нулями и могут содержать подчеркивания для повышения читабельности. Ни один из этих форматов не влияет на фактическое значение литерала:

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

Преобразование типов чисел

Используйте тип Int для всех целых констант и переменных общего назначения в вашем коде, даже если они известны как неотрицательные. Использование стандартного целочисленного типа в повседневных ситуациях означает, что целочисленные константы и переменные будут сразу же совместимы в вашем коде и будут соответствовать типу, выведенному для целых литеральных значений.

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

Преобразование целых чисел

Диапазон чисел, которые могут быть сохранены в целой константе или переменной, отличается для каждого числового типа. Константа или переменная типа Int8 может хранить числа между -128 и 127, в то время как константа или переменная типа UInt8 может хранить числа между 0 и 255. Число, которое не помещается в константу или переменную типа целого числа с указанным размером, будет сообщаться как ошибка при компиляции вашего кода:

let cannotBeNegative: UInt8 = -1
// UInt8 can't store negative numbers, and so this will report an error
let tooBig: Int8 = Int8.max + 1
// Int8 can't store a number larger than its maximum value,
// and so this will also report an error

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

Для преобразования одного числового типа в другой вы инициализируете новое число нужного типа существующим значением. В приведенном ниже примере константа twoThousand имеет тип UInt16, в то время как константа one имеет тип UInt8. Их нельзя складывать напрямую, так как они имеют разные типы. Вместо этого этот пример вызывает UInt16(one), чтобы создать новую UInt16, инициализированную значением one, и использует это значение вместо исходного:

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)

Поскольку обе стороны сложения теперь имеют тип UInt16, сложение разрешено. Выходная константа (twoThousandAndOne) определяется как имеющая тип UInt16, так как это сумма двух значений типа UInt16.

SomeType(ofInitialValue) — это стандартный способ вызова инициализатора типа Swift и передачи начального значения. За кулисами у UInt16 есть инициализатор, принимающий значение UInt8, и поэтому этот инициализатор используется для создания новой UInt16 из существующей UInt8. Однако вы не можете передать любой тип — это должен быть тип, для которого UInt16 предоставляет инициализатор. Расширение существующих типов для предоставления инициализаторов, принимающих новые типы (включая ваши собственные определения типов), описано в Расширениях.

Преобразование между целыми и числами с плавающей точкой

Преобразования между целыми и числами с плавающей точкой должны быть явными:

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double

Здесь значение константы three используется для создания нового значения типа Double, чтобы обе стороны сложения были одного типа. Без этого преобразования сложение не было бы разрешено.

Преобразование чисел с плавающей точкой в целые также должно быть явным. Целый тип может быть инициализирован значением типа Double или Float:

let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int

Значения с плавающей точкой всегда усекаются, когда используются для инициализации нового целого значения таким образом. Это означает, что 4.75 становится 4, а -3.9 становится -3.

Примечание: Правила комбинирования числовых констант и переменных отличаются от правил для числовых литералов. Литеральное значение 3 может быть непосредственно сложено с литеральным значением 0.14159, поскольку числовые литералы сами по себе не имеют явного типа. Их тип выводится только в момент оценки компилятором.

Псевдонимы типов

Псевдонимы типов определяют альтернативное имя для существующего типа. Вы определяете псевдонимы типов с помощью ключевого слова typealias.

Псевдонимы типов полезны, когда вы хотите ссылаться на существующий тип с именем, которое контекстуально более подходящее, например, при работе с данными определенного размера из внешнего источника:

typealias AudioSample = UInt16

После определения псевдонима типа, вы можете использовать псевдоним везде, где можно использовать оригинальное имя:

var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0

Здесь, AudioSample определен как псевдоним для UInt16. Поскольку это псевдоним, вызов AudioSample.min фактически вызывает UInt16.min, который предоставляет начальное значение 0 для переменной maxAmplitudeFound.

Булевы значения

Swift имеет базовый тип булевых значений, называемый Bool. Значения булевых переменных называют логическими, потому что они могут быть только истинными или ложными. Swift предоставляет два константных значения булевого типа, true и false:

let orangesAreOrange = true
let turnipsAreDelicious = false

Типы orangesAreOrange и turnipsAreDelicious были определены как Bool, поскольку они были инициализированы значениями булевых литералов. Как и в случае с Int и Double выше, вам не нужно объявлять константы или переменные как Bool, если вы установите их в true или false сразу после их создания. Вывод типа помогает сделать код Swift более лаконичным и читабельным при инициализации констант или переменных другими значениями, тип которых уже известен.

Булевые значения особенно полезны при работе с условными операторами, такими как оператор if:

if turnipsAreDelicious {
    print("Mmm, tasty turnips!")
} else {
    print("Eww, turnips are horrible.")
}
// Prints "Eww, turnips are horrible."

Условные операторы, такие как оператор if, подробно описаны в ControlFlow.

Безопасность типов Swift предотвращает использование значений, отличных от булевых, вместо Bool. Следующий пример приводит к ошибке на стадии компиляции:

let i = 1
if i {
    // this example will not compile, and will report an error
}

Однако, альтернативный пример ниже является допустимым:

let i = 1
if i == 1 {
    // this example will compile successfully
}

Результат сравнения i == 1 имеет тип Bool, поэтому этот второй пример проходит проверку типа. Сравнения, подобные i == 1, обсуждаются в BasicOperators.

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

Кортежи

Кортежи группируют несколько значений в единое составное значение. Значения внутри кортежа могут быть любого типа и не обязательно должны быть одного и того же типа.

В этом примере, (404, "Not Found") — это кортеж, описывающий код состояния HTTP. Код состояния HTTP — это специальное значение, возвращаемое веб-сервером всякий раз, когда вы запрашиваете веб-страницу. Код состояния 404 Not Found возвращается, если вы запрашиваете веб-страницу, которой не существует.

let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")

Кортеж (404, "Not Found") объединяет Int и String, чтобы предоставить коду состояния HTTP два отдельных значения: число и удобочитаемое описание. Его можно описать как «кортеж типа (Int, String)».

Вы можете создавать кортежи из любой комбинации типов, и они могут содержать любое количество различных типов. Вас ничего не ограничивает в создании кортежа типа (Int, Int, Int) или (String, Bool), или, фактически, любой другой необходимой комбинации.

Вы можете разложить содержимое кортежа на отдельные константы или переменные, которые вы затем можете получить обычным способом:

let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found"

Если вам нужны только некоторые значения кортежа, игнорируйте части кортежа с подчеркиванием (_) при разложении кортежа:

let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"

В качестве альтернативы можно получить значения отдельных элементов кортежа, используя индексы, начиная с нуля:

print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"

Вы можете присваивать имена отдельным элементам кортежа при его определении:

let http200Status = (statusCode: 200, description: "OK")

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

print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"

Кортежи особенно полезны в качестве значений возврата функций. Функция, пытающаяся получить веб-страницу, может вернуть кортеж типа (Int, String) для описания успешности или неудачи получения страницы. Возвращая кортеж с двумя различными значениями, каждый из которых имеет разный тип, функция предоставляет более полезную информацию о своем результате, чем если бы она могла вернуть только одно значение одного типа. Дополнительную информацию см. в <doc:Functions#Functions-with-Multiple-Return-Values>.

Примечание: Кортежи полезны для простых групп связанных значений. Они не подходят для создания сложных структур данных. Если ваша структура данных, скорее всего, будет более сложной, представьте её как класс или структуру, а не как кортеж. Дополнительную информацию см. в ClassesAndStructures.

Необязательные значения

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

В качестве примера значения, которое может отсутствовать, тип Int Swift имеет инициализатор, который пытается преобразовать значение String в значение Int. Однако, не все строки могут быть преобразованы в целые числа. Строка "123" может быть преобразована в числовое значение 123, но строка "hello, world" не имеет соответствующего числового значения. Приведенный ниже пример использует инициализатор для попытки преобразования String в Int:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// The type of convertedNumber is "optional Int"

Поскольку инициализатор в приведенном выше коде может завершиться неудачей, он возвращает необязательное значение Int, а не Int.

Для записи типа необязательного значения вы добавляете знак вопроса (?) после имени типа, содержащегося в необязательном значении — например, тип необязательного Int — это Int?. Необязательное значение Int всегда содержит либо какое-либо Int значение, либо вообще никакого значения. Оно не может содержать ничего другого, например, значение Bool или String.

nil

Вы устанавливаете необязательную переменную в беззнаковое состояние, присвоив ей специальное значение nil:

var serverResponseCode: Int? = 404
// serverResponseCode contains an actual Int value of 404
serverResponseCode = nil
// serverResponseCode now contains no value

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

var surveyAnswer: String?
// surveyAnswer is automatically set to nil

Вы можете использовать оператор if, чтобы выяснить, содержит ли необязательное значение значение, сравнив необязательное значение с nil. Для сравнения используется оператор «равно» (==) или оператор «не равно» (!=).

Если необязательное значение имеет значение, оно считается «не равным» nil:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."

Вы не можете использовать nil с не-необязательными константами или переменными. Если константе или переменной в вашем коде нужно работать с отсутствием значения в определенных условиях, объявите её как необязательное значение соответствующего типа. Константа или переменная, объявленная как не-необязательная, гарантированно никогда не будет содержать значение nil. Если вы попытаетесь присвоить nil ненулевому значению, произойдет ошибка компиляции.

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

При доступе к значению необязательного значения ваш код всегда обрабатывает как nil, так и не-nil случай. Существует несколько действий, которые можно выполнить, когда значение отсутствует, как описано в следующих разделах:

  • Пропустить код, работающий со значением, когда оно nil.

  • Распространить nil значение, вернув nil или используя оператор ?., описанный в OptionalChaining.

  • Предоставить значение по умолчанию, используя оператор ??.

  • Прекратить выполнение программы, используя оператор !.

Примечание: В Objective-C, nil — это указатель на несуществующий объект. В Swift, nil не является указателем — это отсутствие значения определенного типа. Необязательные значения любого типа могут быть установлены в nil, а не только типов объектов.

Связывание необязательных значений

Вы используете связывание необязательных значений, чтобы выяснить, содержит ли необязательное значение значение, и если да, сделать это значение доступным как временную константу или переменную. Связывание необязательных значений может быть использовано с операторами if, guard и while для проверки значения внутри необязательного значения и извлечения этого значения в константу или переменную как часть одного действия. Дополнительную информацию об операторах if, guard и while см. в ControlFlow.

Запишите связывание необязательного значения для оператора if следующим образом:

if let <#constantName#> = <#someOptional#> {
   <#statements#>
}

Вы можете переписать пример possibleNumber из раздела <doc:TheBasics#Optionals>, используя связывание необязательных значений вместо принудительного распаковки:

if let actualNumber = Int(possibleNumber) {
    print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
    print("The string \"\(possibleNumber)\" couldn't be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"

Этот код можно прочитать как:

«Если необязательное значение Int, возвращенное Int(possibleNumber), содержит значение, установите новую константу под названием actualNumber в значение, содержащееся в необязательном значении».

Если преобразование выполнено успешно, константа actualNumber становится доступной для использования в первом ветвлении оператора if. Она уже инициализирована значением, содержащимся в необязательном значении, и имеет соответствующий не-необязательный тип. В этом случае тип possibleNumber — это Int, поэтому тип actualNumber — это Int.

Если вам не нужно ссылаться на исходную необязательную константу или переменную после доступа к содержащемуся в ней значению, вы можете использовать то же имя для новой константы или переменной:

let myNumber = Int(possibleNumber)
// Here, myNumber is an optional integer
if let myNumber = myNumber {
    // Here, myNumber is a non-optional integer
    print("My number is \(myNumber)")
}
// Prints "My number is 123"

Этот код сначала проверяет, содержит ли myNumber значение, как и в предыдущем примере. Если myNumber содержит значение, значение новой константы, названной myNumber, устанавливается в это значение. В теле оператора if запись myNumber относится к этой новой не-необязательной константе. Запись myNumber до или после оператора if относится к исходной необязательной целочисленной константе.

Поскольку такой код встречается часто, вы можете использовать сокращённую запись для разворачивания необязательного значения: просто напишите имя константы или переменной, которую вы разворачиваете. Новая, развернутая константа или переменная неявно используют то же имя, что и необязательное значение.

if let myNumber {
    print("My number is \(myNumber)")
}
// Prints "My number is 123"

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

Вы можете включать любое количество необязательных привязок и логических условий в одном операторе if, разделяя их запятыми. Если любое из значений в необязательных привязках является nil или любое логическое условие оценивается как false, всё условие оператора if считается false. Следующие операторы if эквивалентны:

if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"

if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}
// Prints "4 < 42 < 100"

Константы и переменные, созданные с необязательной привязкой в операторе if, доступны только внутри тела оператора if. В отличие от этого, константы и переменные, созданные с помощью оператора guard, доступны в строках кода, следующих за оператором guard, как описано в <doc:ControlFlow#Early-Exit>.

Предоставление значения по умолчанию

Другой способ обработки отсутствующего значения — предоставление значения по умолчанию с помощью оператора ниль-склеивания (??). Если необязательное значение слева от оператора ?? не является nil, это значение разворачивается и используется. В противном случае используется значение справа от оператора ??. Например, код ниже приветствует кого-то по имени, если имя указано, и использует общее приветствие, если имя является nil.

let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
// Prints "Hello, friend!"

Дополнительную информацию об использовании ?? для предоставления значения по умолчанию см. в <doc:BasicOperators#Nil-Coalescing-Operator>.

Принудительное разворачивание

Когда nil представляет собой необратимую ошибку, такую как ошибка программиста или повреждённое состояние, вы можете получить доступ к основному значению, добавив знак восклицания (!) в конец имени необязательного значения. Это называется принудительным разворачиванием значения необязательного значения. При принудительном разворачивании ненулевого значения результатом является его развернутое значение. Принудительное разворачивание нулевого значения вызывает ошибку во время выполнения.

Оператор ! фактически является сокращённой формой fatalError(_:file:line:). Например, код ниже показывает два эквивалентных подхода:

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

let number = convertedNumber!

guard let number = convertedNumber else {
    fatalError("The number was invalid")
}

Оба варианта кода выше зависят от того, что convertedNumber всегда содержит значение. Запись этого требования в коде с использованием любого из указанных выше подходов позволяет вашему коду проверить, что это требование выполняется во время выполнения.

Дополнительную информацию о принудительном требовании к данным и проверке предположений во время выполнения см. в <doc:TheBasics#Assertions-and-Preconditions>.

Неявные необязательные значения

Как описано выше, необязательные значения указывают, что константа или переменная может иметь «нет значения». Необязательные значения могут проверяться с помощью оператора if, чтобы проверить, существует ли значение, и могут быть условно развернуты с необязательной привязкой, чтобы получить доступ к значению необязательного значения, если оно существует.

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

Эти типы необязательных значений определяются как неявные необязательные значения. Вы пишете неявное необязательное значение, помещая знак восклицания (String!), а не вопросительный знак (String?) после типа, который вы хотите сделать необязательным. Вместо того, чтобы ставить знак восклицания после имени необязательного значения при его использовании, вы ставите знак восклицания после типа необязательного значения при его объявлении.

Неявные необязательные значения полезны, когда значение необязательного значения подтверждается сразу после первоначального определения необязательного значения и определённо может считаться существующим в каждой последующей точке. Основное использование неявных необязательных значений в Swift — во время инициализации классов, как описано в <doc:AutomaticReferenceCounting#Unowned-References-and-Implicitly-Unwrapped-Optional-Properties>.

Не используйте неявные необязательные значения, если есть вероятность, что переменная станет nil в более поздней точке. Всегда используйте обычный тип необязательного значения, если вам нужно проверить значение nil во время существования переменной.

Неявное необязательное значение — это обычное необязательное значение на самом деле, но оно также может использоваться как обычное значение без необходимости разворачивать значение необязательного значения каждый раз при доступе к нему. Следующий пример демонстрирует разницу в поведении между строкой с необязательным значением и строкой с неявным необязательным значением при доступе к их оборачиваемому значению как к явному String:

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // Requires explicit unwrapping

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // Unwrapped automatically

Можно считать, что неявное необязательное значение даёт разрешение на принудительное разворачивание необязательного значения при необходимости. При использовании неявного необязательного значения Swift сначала пытается использовать его как обычное необязательное значение; если его нельзя использовать как необязательное значение, Swift принудительно разворачивает значение. В коде выше значение необязательного значения assumedString принудительно разворачивается перед присвоением его значения implicitString, так как implicitString имеет явный тип без необязательных значений типа String. В нижеприведённом коде optionalString не имеет явного типа, поэтому это обычное необязательное значение.

let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.

Если неявное необязательное значение является nil, и вы пытаетесь получить доступ к его оборачиваемому значению, будет вызвана ошибка во время выполнения. Результат точно такой же, как если бы вы написали знак восклицания, чтобы принудительно развернуть обычное необязательное значение, которое не содержит значения.

Вы можете проверить, является ли неявное необязательное значение nil, точно так же, как вы проверяете обычное необязательное значение:

if assumedString != nil {
    print(assumedString!)
}
// Prints "An implicitly unwrapped optional string."

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

if let definiteString = assumedString {
    print(definiteString)
}
// Prints "An implicitly unwrapped optional string."

Обработка ошибок

Вы используете обработку ошибок, чтобы реагировать на условия ошибок, которые может встретить ваша программа во время выполнения.

В отличие от необязательных значений, которые могут использовать наличие или отсутствие значения для передачи успешного или неудачного выполнения функции, обработка ошибок позволяет определить основную причину неудачи и при необходимости распространить ошибку в другую часть вашей программы.

Когда функция сталкивается с условием ошибки, она выбрасывает ошибку. Затем вызывающий объект этой функции может перехватить ошибку и соответствующим образом отреагировать.

func canThrowAnError() throws {
    // this function may or may not throw an error
}

Функция указывает, что может выбросить ошибку, включив ключевое слово throws в своё объявление. При вызове функции, которая может выбросить ошибку, вы предваряете выражение ключевым словом try.

Swift автоматически распространяет ошибки из текущей области видимости до тех пор, пока они не будут обработаны фрагментом catch.

do {
    try canThrowAnError()
    // no error was thrown
} catch {
    // an error was thrown
}

Оператор do создаёт новую содержащую область видимости, которая позволяет распространять ошибки в один или несколько фрагментов catch.

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

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

В этом примере функция makeASandwich() выбросит ошибку, если нет чистых тарелок или если какие-либо ингредиенты отсутствуют. Поскольку функция makeASandwich() может выбросить ошибку, вызов функции заключён в выражение try. Заключая вызов функции в оператор do, любые выброшенные ошибки будут переданы в указанные фрагменты catch.

Если ошибка не выброшена, вызывается функция eatASandwich(). Если ошибка выброшена и она соответствует случаю SandwichError.outOfCleanDishes, вызывается функция washDishes(). Если ошибка выброшена и она соответствует случаю SandwichError.missingIngredients, вызывается функция buyGroceries(_:) с ассоциированным значением [String], захваченным шаблоном catch.

Более подробное описание выброса, перехвата и распространения ошибок приведено в ErrorHandling.

Утверждения и предварительные условия

Утверждения и предварительные условия — это проверки, которые происходят во время выполнения. Вы используете их, чтобы убедиться, что выполняется необходимое условие, прежде чем выполнять любой дальнейший код. Если логическое условие в утверждении или предварительном условии оценивается как true, выполнение кода продолжается как обычно. Если условие оценивается как false, текущее состояние программы недопустимо; выполнение кода завершается, и ваше приложение завершается.

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

Помимо проверки ваших ожиданий во время выполнения, утверждения и предварительные условия также становятся полезной формой документации в коде. В отличие от условий ошибок, обсуждаемых в <doc:TheBasics#Error-Handling> выше, утверждения и предварительные условия не используются для восстанавливаемых или ожидаемых ошибок. Поскольку сбой утверждения или предварительного условия указывает на недопустимое состояние программы, нет способа перехватить сбой утверждения. Восстановление из недопустимого состояния невозможно. Когда утверждение терпит неудачу, по крайней мере одна часть данных программы становится недействительной — но вы не знаете, почему она недействительна, или является ли также недопустимым другое состояние.

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

Разница между утверждениями и предварительными условиями заключается в том, когда они проверяются: утверждения проверяются только в отладочных сборках, а предварительные условия — и в отладочных, и в производственных. В производственных сборках условие внутри утверждения не оценивается. Это означает, что вы можете использовать столько утверждений, сколько захотите, во время процесса разработки, не влияя на производительность в производственной среде.

Отладка с помощью утверждений

Вы записываете утверждение, вызвав функцию assert(_:_:file:line:) из стандартной библиотеки Swift. Вы передаете этой функции выражение, которое вычисляется до true или false, и сообщение для отображения, если результат условия равен false. Например:

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 isn't >= 0.

В этом примере выполнение кода продолжается, если age >= 0 вычисляется до true, то есть, если значение age неотрицательно. Если значение age отрицательно, как в примере кода выше, то age >= 0 вычисляется до false, и утверждение терпит неудачу, завершая работу приложения.

Вы можете опустить сообщение об утверждении — например, когда оно просто повторяет условие как текст.

assert(age >= 0)

Если код уже проверяет условие, вы используете функцию assertionFailure(_:file:line:), чтобы указать, что утверждение завершилось неудачей. Например:

if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

Принудительное применение предварительных условий

Используйте предварительное условие всякий раз, когда условие может быть ложным, но обязательно должно быть истинным для продолжения выполнения вашего кода. Например, используйте предварительное условие для проверки того, что индекс не выходит за пределы диапазона, или для проверки того, что функции был передан допустимый параметр.

Вы записываете предварительное условие, вызвав функцию precondition(_:_:file:line:). Вы передаете этой функции выражение, которое вычисляется до true или false, и сообщение для отображения, если результат условия равен false. Например:

// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")

Вы также можете вызвать функцию preconditionFailure(_:file:line:), чтобы указать, что произошла ошибка — например, если был выбран необязательный случай оператора switch, но все допустимые входные данные должны были быть обработаны одним из других случаев оператора switch.

Примечание: если вы компилируете в режиме без проверки (-Ounchecked), предварительные условия не проверяются. Компилятор предполагает, что предварительные условия всегда истинны, и оптимизирует ваш код соответственно. Однако функция fatalError(_:file:line:) всегда останавливает выполнение, независимо от настроек оптимизации.

Вы можете использовать функцию fatalError(_:file:line:) во время прототипирования и ранней разработки, чтобы создать заглушки для функций, которые еще не реализованы, написав fatalError("Unimplemented") как реализацию заглушки. Поскольку фатальные ошибки никогда не исключаются из оптимизации, в отличие от утверждений или предварительных условий, вы можете быть уверены, что выполнение всегда остановится, если оно встретит реализацию заглушки.

This source file is part of the Swift.org open source project
Copyright © 2014 - 2025 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API