Документация

Типы

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

Типы данных, которые обычно считаются основными или примитивными в других языках, такие как типы, которые представляют цифры, символы и строки-фактически именованные типы, определены и реализованы в стандартной библиотеке Swift с использованием структур. Так как это именованные типы, вы можете расширить их поведение для удовлетворения потребностей вашей программы, используя объявление расширения, которое рассматривается в главе "Расширения".

Составной тип представляет собой тип без имени, и определяется самостоятельно в языке Swift. Есть два составных типа: типы функций и типы кортежей. Составной тип может содержать именованные типы и другие составные типы. Например, тип кортежа (Int, (Int, Int)) содержит два элемента: первый именованный тип Int, а второй составной тип (Int, Int).

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

Грамматика типа

type → array-type
type → dictionary-type­
type → function-type­
type → type-identifier­
type → tuple-type
type → optional-type­
type → implicitly-unwrapped-optional-type­
type → protocol-composition-type
type → metatype-type
type → Any
type → Self
type → ( type )

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

Аннотация типа явно указывает на тип переменной или выражения. Аннотации типов начинаются с двоеточия (:) и заканчиваются именем типа, как показано в следующем примере:

let someTuple: (Double, Double) = (3.14159, 2.71828)
func someFunction(a: Int) { /* ... */ }

В первом примере выражению someTuple предписано иметь тип кортежа (Double, Double). Во втором примере, параметру a функции someFunction предписано иметь тип Int.

Аннотации типа могут содержать дополнительный список атрибутов типа перед типом.

Грамматика аннотации типа

type-annotation → attributes­opt­  inoutopt­ type­

Идентификатор типа

Идентификатор типа относится либо к именованному типу, либо к псевдониму именованного или составного типа.

По большей части, идентификатор типа непосредственно ссылается на именованный тип с тем же именем, что и идентификатор. Например, Int является идентификатором типа, который непосредственно ссылается на именованный тип Int, и идентификатор типа Dictionary<String, Int> непосредственно ссылается на именованный тип Dictionary<String, Int>.

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

typealias Point = (Int, Int)
let origin: Point = (0, 0)

Во втором случае, идентификатор типа использует точку (.) для обозначения именованных типов, объявленных в других модулях или вложенных внутрь других типов. Например, идентификатор типа в следующем коде ссылается на именованный тип MyType, объявленный в модуле ExampleModule.

var someValue: ExampleModule.MyType

Грамматика идентификатора типа

type-identifier → type-name­  generic-argument-clause­opt­  |  type-name­  generic-argument-clause­opt­  type-identifier­ type-name → identifier­

Тип кортежа

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

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

Если элемент кортежа имеет имя, то имя является частью типа:

var someTuple = (top: 10, bottom: 12)  // someTuple имеет тип (top: Int, bottom: Int)
someTuple = (top: 4, bottom: 42) // OK: имена совпадают
someTuple = (9, 99)              // OK: имена выводятся
someTuple = (left: 5, right: 5)  // Error: имена не совпадают

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

Грамматика кортежного типа

tuple-type → tuple-type-body­opt­ tuple-type-element-list → tuple-type-element­  |  tuple-type-element­tuple-type-element-list­ tuple-type-element →  ­element-name   ­type-annotation­  ­|  type­ element-name → identifier­

Функциональный тип

Тип функции представляет собой тип функции, метода или замыкания и состоит из параметра и возвращаемого типа, разделенного стрелкой (->):

тип параметра -> возвращаемый тип

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

Параметр типа функции () -> T (где T произвольный тип) может применять атрибут autoclosure для неявного создания замыкания для своих вызовов. Это обеспечивает синтаксически удобный способ отложить вычисление выражения, без необходимости написания явного замыкания при вызове функции. Подробнее об autoclosure в разделе Автозамыканиях.

У функциональных типов может быть параметр с переменным числом аргументов (вариативный). Синтаксически, такой параметр состоит из имени базового типа, за которым сразу следует троеточие (...), как в Int... . Вариативный параметр рассматривается как массив, содержащий элементы базового типа. Например, вариативный параметр Int... рассматривается как [Int]. Подробнее о Вариативных параметрах.

Для того, чтобы указать параметр In-Out, приставьте тип параметра с ключевым словом inout. Вы не можете обозначать вариативный параметр или возвращаемый тип ключевым словом inout. In-Out параметры подробнее рассматриваются в разделе Сквозные параметры.

Имена аргументов не являются частью типа соответствующей функции:

func someFunction(left: Int, right: Int) {}
func anotherFunction(left: Int, right: Int) {}
func functionWithDifferentLabels(top: Int, bottom: Int) {}
 
var f = someFunction // The type of f is (Int, Int) -> Void, not (left: Int, right: Int) -> Void.
f = anotherFunction              // OK
f = functionWithDifferentLabels  // OK
 
func functionWithDifferentArgumentTypes(left: Int, right: String) {}
f = functionWithDifferentArgumentTypes     // Ошибка

func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
 
f = functionWithDifferentNumberOfArguments // Ошибка

Поскольку аргументы Labels не являются частью типа функции, вы опускаете их при написании типа функции.

var operation: (lhs: Int, rhs: Int) -> Int     // Error
var operation: (_ lhs: Int, _ rhs: Int) -> Int // OK
var operation: (Int, Int) -> Int               // OK

Если тип функции включает в себя более чем одну стрелку (->), типы функции группируются справа налево. Например, тип функции Int -> Int -> Int понимается как Int -> (Int -> Int), то есть функция, которая принимает Int и возвращает другую функцию, которая принимает и возвращает Int.

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

Ограничения для несбегающих замыканий

Параметр, который является несобранной функцией, не может быть сохранен в свойстве, переменной или константе типа Any, поскольку это может позволить вывести значение.

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

let external: (() -> Void) -> Void = { _ in () }
func takesTwoFunctions(first: (() -> Void) -> Void, second: (() -> Void) -> Void) {
    first { first {} }       // Ошибка
    second { second {}  }    // Ошибка

    first { second {} }      // Ошибка
    second { first {} }      // Ошибка

    first { external {} }    // OK
    external { first {} }    // OK
}

В приведенном выше коде оба параметра, которые принимают значения TwoFunctions (first: second:), являются функциями. Ни один из параметров не отмечен как @escaping, поэтому они оба не отображаются в результате.

Четыре вызова функций, отмеченные «Ошибка» в приведенном выше примере, вызывают ошибки компилятора. Поскольку первый и второй параметры являются не ускользающими функциями, они не могут передаваться в качестве аргументов другому параметру не ускользающей функции. Напротив, два вызова функций, отмеченные «ОК», не вызывают ошибки компилятора. Эти вызовы функций не нарушают ограничение, потому что внешний не является одним из параметров takeTwoFunctions(first: second : ).

Если вам нужно избегать этого ограничения, отметьте один из параметров как экранирование или временно конвертируйте один из параметров функции в функцию экранирования с помощью функции withoutActuallyEscaping (_: do:). Информацию об избежании конфликта доступа к памяти см. В разделе Безопасность хранения.

Грамматика функционального типа

function-type → attributes­opt  ­function-type-argument-clause­  throws­opt­  ->­  type­ function-type → attributes­opt­  function-type-argument-clause­  rethrows­  ->­  type­ function-type-argument-clause →  function-type-argument-clause → function-type-argument-list­...­opt­ function-type-argument-list → function-type-argument­ | function-type-argument­ , ­function-type-argument-list­ function-type-argument → attributes­opt­  inout­opt­  type­ | argument-label­  type-annotation­ argument-label → identifier­

Типы массива

Язык Swift предоставляет следующий синтаксический сахар для стандартной библиотеки Swift тип Array<Element>:

[type]

Другими словами, следующие два объявления эквивалентны:

let someArray: Array<String> = ["Alex", "Brian", "Dave"]
let someArray: [String] = ["Alex", "Brian", "Dave"]

В обоих случаях константа someArray объявляется как массив строк. Элементы массива могут быть доступны через индексацию, указав допустимое значение индекса в квадратных скобках: someArray[0] относится к элементу с индексом 0, "Alex".

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

var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

При обращении к элементам в многомерном массиве, самый левый индекс сабскриптов относится к элементу с этим индексом в самом нижнем массиве. Следующий индекс сабскрипта с правой стороны относится к элементу с этим индексом в массиве, вложенном на предыдущем уровне. И так далее. Это означает, что в приведенном выше примере, array3D[0] относится к [[1, 2], [3, 4]], array3D[0][1] относится к [3, 4], и array3D[0][1][1] относится к значению 4.

Более подробно о стандартном типе Array библиотеки Swift, см. "Массивы".

Грамматика типа массива

array-type → type­

Тип словаря

Язык Swift предоставляет следующий синтаксический сахар для типа Dictionary<Key, Value> стандартной библиотеки Swift:

[key type: value type]

Другими словами, следующие два объявления эквивалентны:

let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]
let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]

В обоих случаях константа someDictionary объявляется как словарь со строками в качестве ключей и целых чисел в качестве значений.

Значения словаря могут быть доступны через индексацию при указании соответствующего ключа в квадратных скобках: someDictionary["Alex"] относится к значению, связанному с ключом "Alex". Сабскрипт возвращает опциональное значение типа значения словаря. Если указанный ключ не содержится в словаре, сабскрипт возвращает nil.

Основной тип ключа словаря должен соответствовать протоколу Hashable стандартной библиотеки Swift.

Подробнее см. Словари.

Грамматика типа словаря

dictionary-type type­ :­ type­

Опциональный тип

Язык Swift определяет постфикс ? как синтаксический сахар для именованного типа  Optional<Wrapped>, определенного в стандартной библиотеке Swift. Другими словами, следующие два объявления эквивалентны:

var optionalInteger: Int?
var optionalInteger: Optional<Int>

В обоих случаях переменная optionalInteger объявляется так, чтобы иметь тип опционального Int. Обратите внимание, что никаких пробелов не должно появляться между именем типа и ?.

Тип Optional<Wrapped> является перечислением с двумя кейсами, None и Some(Wrapped), которые используются для представления значений, которые могут присутствовать, а могут и не присутствовать. Любой тип может быть явно объявлен (или неявно преобразован) так, чтобы быть опциональным типом. Если вы не укажете первоначальное значение при объявлении опциональной переменной или свойства, его значение автоматически устанавливается на nil.

Если экземпляр опционального типа содержит значение, вы можете получить доступ к этому значению, используя постфиксный оператор !, как показано ниже:

optionalInteger = 42
optionalInteger! // 42

Использование оператора ! для извлечения опционала, имеющего значение nil, приводит к ошибке во время выполнения.

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

Для получения дополнительной информации см. Опционалы.

Грамматика опционального типа

optional-type → type­  

Неявно извлеченный опциональный тип

Язык Swift определяет постфикс ! как синтаксический сахар для именованного типа ImplicitlyUnwrappedOptional<Wrapped>, определенного в стандартной библиотеке Swift. Другими словами, следующие два объявления эквивалентны:

var implicitlyUnwrappedString: String!
var explicitlyUnwrappedString: Optional<String>

Обратите внимание, что не должно появиться никаких пробелов между именем типа и !.

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

let tupleOfImplicitlyUnwrappedElements: (Int!, Int!)  // Ошибка
let implicitlyUnwrappedTuple: (Int, Int)!             // OK
 
let arrayOfImplicitlyUnwrappedElements: [Int!]        // Ошибка
let implicitlyUnwrappedArray: [Int]!                  // OK

Так как неявно извлеченные опционалы имеют тот же тип Optional<Wrapped>, то можете использовать неявно извлеченные опционалы в тех же местах в вашем коде, в которых можно использовать опционалы. Например, вы можете присвоить значения неявно извлеченных опционалов переменным, константам и свойствам опционалов и наоборот.

Как и с опционалами, если вы не обеспечиваете первоначальное значение при объявлении неявно распакованной опциональной переменной или свойства, его значение автоматически устанавливается на nil.

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

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

Для получения дополнительной информации см. в разделе Неявно извлеченные опционалы.

Грамматика неявно извлеченного опционального типа

implicitly-unwrapped-optional-type → type ­

Тип композиции протоколов

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

Тип композиции протоколов выглядит следующим образом:

Protocol 1Protocol 2

Тип композиции протоколов позволяет указать значение, тип которого соответствует требованиям нескольких протоколов без необходимости явно определять новый, именованный протокол, который наследует от каждого протокола, которому вы хотите, чтобы тип соответствовал. Например, указать тип композиции протоколов ProtocolA & ProtocolB & ProtocolC так же эффективно, как определить новый протокол, который наследует от ProtocolA, ProtocolB, и ProtocolC, но без необходимости введения нового имени. Так же вы можете использовать SuperClass & ProtocolA вместо того, чтобы определять новый протокол, который является подклассом SuperClass и соответствует ProtocolA.

Каждый элемент в списке композиции протокола должен быть (этот список может иметь всего один класс):

  • имя класса
  • имя протокола
  • псеводоним типа, где внутренний тип является либо композицией протокола, либо самим протоколом, либо классом.

Когда композиция протокола содержит псевдонимы типа, то не исключено, что один и тот же протокол может появляться не один раз. В таких случаях повторения протокола игнорируются. Например, определение PQR в коде ниже аналогично P & Q & R.

typealias PQ = P & Q
typealias PQR = PQ & Q & R

Грамматика типа композиции протоколов

protocol-composition-type → protocol-identifier­ & ­protocol-composition-continuation­ protocol-composition-continuation → protocol-identifier­  | protocol-composition-type­ protocol-identifier → type-identifier­

Тип метатипа

Тип метатипа относится к типу любого типа, в том числе к типам класса, типам конструкций, типам перечислений и типам протоколов.

Метатип типа класса, структуры или перечисления является именем этого типа, за которым идет .Type. Метатип типа протокола - не конкретный тип, который соответствует протоколу во время выполнения - это имя этого протокола с последующим .Protocol. Например, метатип типа класса SomeClass является SomeClass.Type и метатип протокола SomeProtocol является SomeProtocol.Protocol.

Вы можете использовать выражение постфикса self для доступа к типу в качестве значения. Например, SomeClass.self возвращает сам SomeClass, а не экземпляр SomeClass. И SomeProtocol.self возвращает сам SomeProtocol, а не экземпляр типа, который соответствует SomeProtocol во время выполнения задачи. Вы можете использовать функцию type(of:) с экземпляром типа для доступа к экземпляру динамической идентификации типа в период выполнения, в качестве значения, как показано в следующем примере:

class SomeBaseClass {
    class func printClassName() {
        print("SomeBaseClass")
    }
}
class SomeSubClass: SomeBaseClass {
    override class func printClassName() {
        print("SomeSubClass")
    }
}
let someInstance: SomeBaseClass = SomeSubClass()
// someInstance имеет тип во время компиляции SomeBaseClass,
// во время исполнения типом someInstance является SomeSubClass
type(of: someInstance).printClassName()
// Выведет "SomeSubClass"

Для получения дополнительной информации, см. type (of: ) стандартной библиотеке Swift.

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

class AnotherSubClass: SomeBaseClass {
    let string: String
    required init(string: String) {
        self.string = string
    }
    override class func printClassName() {
        print("AnotherSubClass")
    }
}
let metatype: AnotherSubClass.Type = AnotherSubClass.self
let anotherInstance = metatype.init(string: "some string")

Грамматика метатипа

metatype-type → type­ .­ Type­  |  type­. ­ Protocol­

Наследование типа

Наследование типа используется для указания того, у какого класса наследует именованный тип и каким протоколам он соответствует. Наследование типа также используется для указания классовых требований по протоколу. Наследование типа начинается с двоеточия (:), за которым следуют либо требования class, либо список идентификаторов типа, либо и то и то.

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

Другие именованные типы могут только наследовать или соответствовать списку протоколов. Типы протоколов могут наследовать от любого количества других протоколов. Когда тип протокола наследует от других протоколов, и требования от всех протоколов собираются в совокупность, и любой тип, наследующий от текущего протокола, должен соответствовать всем этим требованиям. Как обсуждалось ранее, вы можете включить ключевое слово class в качестве первого пункта в условие типа наследования, чтобы отметить объявление протокола с class-требованием.

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

Грамматика наследования типа

type-inheritance-clause → :­ class-requirement­type-inheritance-list­ type-inheritance-clause → :­ class-requirement­ type-inheritance-clause → :­ type-inheritance-list­ type-inheritance-list → type-identifier­  |  type-identifier­ , ­type-inheritance-list­ class-requirement → class­

Вывод типа

Swift широко использует вывод типа, что позволяет опустить тип или часть типа множества переменных и выражений в коде. Например, вместо написания var x: Int = 0, вы можете написать var x = 0, опуская тип полностью, компилятор правильно понимает, что x это значение типа Int. Кроме того, вы можете опустить часть типа, когда полный тип может быть выведен из контекста. Например, когда вы пишете let dict: Dictionary = ["A": 1], компилятор делает вывод, что dict имеет тип Dictionary<String, Int>.

В обоих приведенных выше примерах информация о типе передается от листьев дерева выражения к его корню. То есть, для типа x в var x: Int = 0 сначала проводится проверка типа 0, а затем эта информация передается корню (переменной x).

В Swift, информация о типе может также протекать в обратном направлении: от корня к листьям. В следующем примере, явный тип аннотации (: Float) на константе eFloat приводит к тому, что у числовой константы 2,71828 выведенный тип Float вместо Double.

let e = 2.71828 // Выведенным типом e является Double.
let eFloat: Float = 2.71828 // Выведенным типом eFloat является Float.

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

 

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: