Приведение типов - это способ проверки типа экземпляра или обработки этого экземпляра как другого суперкласса или подкласса где-то еще в его собственной иерархии классов.

Приведение типов в Swift реализуется с isи asоператорами. Эти два оператора предоставляют простой и выразительный способ проверить тип значения или привести значение к другому типу.

Определение иерархии классов для приведения типов

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

Первый фрагмент определяет новый базовый класс с именем MediaItem. Этот класс предоставляет базовые функциональные возможности для любого вида элемента, который появляется в библиотеке цифрового мультимедиа. В частности, он объявляет nameсвойство типа Stringи инициализатор. (Предполагается, что все элементы мультимедиа, включая все фильмы и песни, будут иметь название.)init name

class MediaItem {

var name: String

init(name: String) {

self.name = name

}

}

Следующий фрагмент определяет два подкласса MediaItem. Первый подкласс Movieсодержит дополнительную информацию о фильме или фильме. Он добавляет directorсвойство поверх базового MediaItemкласса с соответствующим инициализатором. Второй подкласс Songдобавляет artistсвойство и инициализатор поверх базового класса:

class Movie: MediaItem {

var director: String

init(name: String, director: String) {

self.director = director

super.init(name: name)

}

}



class Song: MediaItem {

var artist: String

init(name: String, artist: String) {

self.artist = artist

super.init(name: name)

}

}

Последний фрагмент создает постоянный массив с именем library, который содержит два Movieэкземпляра и три Songэкземпляра. Тип libraryмассива определяется путем его инициализации содержимым литерала массива. Средство проверки типов Swift может вывести это Movieи Songиметь общий суперкласс MediaItem, и поэтому оно выводит тип [MediaItem]для libraryмассива:

let library = [

Movie(name: "Casablanca", director: "Michael Curtiz"),

Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),

Movie(name: "Citizen Kane", director: "Orson Welles"),

Song(name: "The One And Only", artist: "Chesney Hawkes"),

Song(name: "Never Gonna Give You Up", artist: "Rick Astley")

]

// the type of "library" is inferred to be [MediaItem]

Предметы, хранящиеся в library, все еще Movieи Songэкземпляры за кадром. Однако, если вы перебираете содержимое этого массива, элементы, которые вы получаете обратно, печатаются как MediaItem, а не как Movieили Song. Чтобы работать с ними как с их собственным типом, необходимо проверить их тип или уменьшить их до другого типа, как описано ниже.

Проверка типа

Используйте оператор проверки типа ( is), чтобы проверить, принадлежит ли экземпляр определенному типу подкласса. Оператор проверки типа возвращает, trueесли экземпляр принадлежит к этому типу подкласса, и falseесли это не так.

Приведенный ниже пример определяет две переменные, movieCountи songCount, которые подсчитывают количество Movieи Songэкземпляры в libraryмассиве:

var movieCount = 0

var songCount = 0



for item in library {

if item is Movie {

movieCount += 1

} else if item is Song {

songCount += 1

}

}



print("Media library contains \(movieCount) movies and \(songCount) songs")

// Prints "Media library contains 2 movies and 3 songs"

Этот пример перебирает все элементы в libraryмассиве. На каждом проходе цикл forinустанавливает itemконстанту для следующего MediaItemв массиве.

item is Movieвозвращает, trueесли текущий MediaItemявляется Movieэкземпляром, и falseесли это не так. Аналогичным образом проверяется, является ли элемент экземпляром. В конце - петли, значения и содержат счет того , сколько экземпляров были обнаружены каждого типа.item is SongSongforinmovieCountsongCountMediaItem

Понижающее приведение

Константа или переменная определенного типа класса может фактически ссылаться на экземпляр подкласса за кулисами. Если вы считаете, что это так, вы можете попытаться выполнить переход к типу подкласса с помощью оператора приведения типа ( as?или as!).

Поскольку принижение может быть неудачным, оператор приведения типов может быть двух разных форм. Условная форма, as?возвращает необязательное значение типа, который вы пытаетесь уменьшить. Принудительная форма, as!пытается выполнить снижение и принудительно разворачивает результат как одно сложное действие.

Используйте условную форму оператора приведения типа ( as?), если вы не уверены, что преобразование будет выполнено успешно. Эта форма оператора всегда будет возвращать необязательное значение, и это значение будет, nilесли снижение было невозможно. Это позволяет вам проверять успешное снижение.

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

В приведенном ниже пример итерации по каждому MediaItemв library, и выводит соответствующее описание для каждого пункта. Для этого ему необходимо получить доступ к каждому элементу как true Movieили Song, а не просто как MediaItem. Это необходимо для того, чтобы иметь возможность получить доступ к directorили artistсвойству Movieили Songдля использования в описании.

В этом примере каждый элемент в массиве может быть Movieили может быть Song. Вы не знаете заранее, какой фактический класс использовать для каждого элемента, и поэтому целесообразно использовать условную форму оператора приведения типа ( as?), чтобы каждый раз проверять принижение в цикле:

for item in library {

if let movie = item as? Movie {

print("Movie: \(movie.name), dir. \(movie.director)")

} else if let song = item as? Song {

print("Song: \(song.name), by \(song.artist)")

}

}



// Movie: Casablanca, dir. Michael Curtiz

// Song: Blue Suede Shoes, by Elvis Presley

// Movie: Citizen Kane, dir. Orson Welles

// Song: The One And Only, by Chesney Hawkes

// Song: Never Gonna Give You Up, by Rick Astley

Пример начинается с попытки уменьшить текущее значение itemкак Movie. Поскольку itemэто MediaItemпример, возможно, что это может быть Movie; в равной степени возможно также, что это может быть Songили даже просто база MediaItem. Из-за этой неопределенности as?форма оператора приведения типа возвращает необязательноезначение при попытке перехода к типу подкласса. Результат имеет тип или «необязательный ».item as? MovieMovie?Movie

Снижение до Movieсбоя при применении к Songэкземплярам в массиве библиотеки. Чтобы справиться с этим, в приведенном выше примере используется необязательная привязка, чтобы проверить, действительно ли необязательная Movieсодержит значение (то есть, чтобы выяснить, прошел ли переход вниз). Эта необязательная привязка записывается как « », которую можно прочитать как:if let movie = item as? Movie

«Попробуй доступ itemкак Movie. Если это успешно, установите новую временную константу, вызываемую movieк значению, сохраненному в возвращаемом дополнительном Movie. ”

Если даункастинг завершается успешно, свойства movieзатем используются для печати описания этого Movieэкземпляра, включая его имя director. Аналогичный принцип используется для проверки Songэкземпляров и для печати соответствующего описания (включая artistимя) всякий раз, когда Songв библиотеке находится a .

ЗАМЕТКА

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

Приведение типов для Any и AnyObject

Swift предоставляет два специальных типа для работы с неспецифическими типами:

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

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

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

var things = [Any]()

things.append(0)

things.append(0.0)

things.append(42)

things.append(3.14159)

things.append("hello")

things.append((3.0, 5.0))

things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))

things.append({ (name: String) -> String in "Hello, \(name)" })

thingsМассив содержит два Intзначения, два Doubleзначения, а Stringзначение, кортеж типа , фильм «Гостбастерс», а выражение закрытия , которая принимает значение и возвращает другое значение.(Double, Double)StringString

Чтобы обнаружить конкретный тип константы или переменной, которая, как известно, имеет тип Anyили AnyObject, вы можете использовать шаблон isили asв switchслучаях оператора. Приведенный ниже пример перебирает элементы в thingsмассиве и запрашивает тип каждого элемента с помощью switchоператора. Несколько случаев switchоператора связывают их совпадающее значение с константой указанного типа, чтобы позволить печатать его значение:

for thing in things {

switch thing {

case 0 as Int:

print("zero as an Int")

case 0 as Double:

print("zero as a Double")

case let someInt as Int:

print("an integer value of \(someInt)")

case let someDouble as Double where someDouble > 0:

print("a positive double value of \(someDouble)")

case is Double:

print("some other double value that I don't want to print")

case let someString as String:

print("a string value of \"\(someString)\"")

case let (x, y) as (Double, Double):

print("an (x, y) point at \(x), \(y)")

case let movie as Movie:

print("a movie called \(movie.name), dir. \(movie.director)")

case let stringConverter as (String) -> String:

print(stringConverter("Michael"))

default:

print("something else")

}

}



// zero as an Int

// zero as a Double

// an integer value of 42

// a positive double value of 3.14159

// a string value of "hello"

// an (x, y) point at 3.0, 5.0

// a movie called Ghostbusters, dir. Ivan Reitman

// Hello, Michael

ЗАМЕТКА

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

let optionalNumber: Int? = 3

things.append(optionalNumber) // Warning

things.append(optionalNumber as Any) // No warning