Видеокурсы по изучению языка программирования Swift. Подробнее

Типы

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

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

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

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

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

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

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

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

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

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 является типом псевдонима для пустого типа кортежа, (). Если внутри скобок только один элемент, то тип-это просто тип этого элемента. Например, тип (Int) является Int, а не (Int). В результате, вы можете назвать элемент кортежа только тогда, когда тип кортежа имеет два или более элементов.

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

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) {}
func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
 
f = functionWithDifferentArgumentTypes     // Ошибка
f = functionWithDifferentNumberOfArguments // Ошибка

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

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

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

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 так же эффективно, как определить новый протокол ProtocolD, который наследует от ProtocolA, ProtocolB, и ProtocolC, но без необходимости введения нового имени.

Каждый элемент в списке композиции протокола должен быть либо именем протокола или псевдонимом типа типа композиции протокола. Если список пуст, это указывает на пустой тип композиции протоколов, которому соответствует каждый тип.

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

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 во время выполнения задачи. Вы можете использовать выражение dynamicType с экземпляром типа для доступа к экземпляру динамической идентификации типа в период выполнения, в качестве значения, как показано в следующем примере:

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"

Используйте операторы тождественности (=== и !==) для того чтобы проверить, совпадает ли тип экземпляра во время выполнения с типом во время компиляции.

if type(of: someInstance) === someInstance.self {
    print("The dynamic and static type of someInstance are the same")
} else {
    print("The dynamic and static type of someInstance are different")
}
// Prints "The dynamic and static type of someInstance are different"

Используйте выражение инициализатора для того, чтобы создать экземпляр типа из значения типа этого метатипа. Для экземпляров класса инициализатор, который вызывается, должен быть отмечен ключевым словом 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 работает на уровне одного выражения или утверждения. Это означает, что вся информация, необходимая для вывода пропущенного типа или части типа в выражении должна быть доступна из проверки типов выражения или одного из его подвыражений.

 

Swift: 
3.0