Swift предлагает два специальных псевдонима, позволяющих работать с неопределенными типами:
• AnyObject соответствует произвольному экземпляру любого класса;
• Any соответствует произвольному типу данных.
Данные псевдонимы позволяют корректно обрабатывать ситуации, когда конкретное наименование типа или класса неизвестно либо набор возможных типов может быть разнородным.
Благодаря псевдониму Any можно создавать хранилища неопределенного типа данных. Объявим массив, который может содержать элементы произвольных типов (листинг 28.1).
Листинг 28.1
var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append("hello")
things.append((3.0, 5.0))
things.append({ (name: String) -> String in "Hello, \(name)" })
Массив things содержит значения типов: Int, Double, String, (Double, Double) и (String)->String. То есть перед вами целый набор различных типов данных.
При запросе любого из элементов массива вы получите значение не того типа данных, который предполагался при установке конкретного значения, а типа Any.
ПРИМЕЧАНИЕ Псевдоним Any несовместим с протоколом Hashable, поэтому использование типа Any там, где необходимо сопоставление, невозможно. Это относится, например, к ключам словарей.
Для анализа каждого элемента массива необходимо выполнить приведение типа. Так вы сможете получить каждый элемент, преобразованный в его действительный тип данных.
Рассмотрим пример, в котором разберем объявленный ранее массив поэлементно и определим тип данных каждого элемента (листинг 28.2).
Листинг 28.2
for thing in things {
switch thing {
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 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 stringConverter as (String) -> String:
print(stringConverter("Troll"))
default:
print("something else")
}
}
Консоль
an integer value of 0
something else
an integer value of 42
a string value of "hello"
an (x, y) point at 3.0, 5.0
Hello, Troll
Каждый из элементов массива преобразуется в определенный тип при помощи оператора as. При этом в конструкции switch-case данный оператор не требует указывать какой-либо постфикс (знак восклицания или вопроса).
Псевдоним AnyObject позволяет указать на то, что в данном месте должен или может находиться экземпляр любого класса. При этом вы будете довольно часто встречать массивы данного типа при разработке программ с использованием фреймворка Cocoa Foundation. Данный фреймворк написан на Objective-C, а этот язык не имеет массивов с явно указанными типами данных.
Объявим массив экземпляров с помощью псевдонима AnyObject (листинг 28.3).
Листинг 28.3
let someObjects: [AnyObject] = [Dog(), NoisyDog(), Dog()]
При использовании псевдонима AnyObject нет ограничений на использование классов только из одной иерархической структуры. В данном примере если вы извлекаете произвольный элемент массива, то получаете экземпляр класса AnyObject, не имеющий свойств и методов для взаимодействия с ним.
Порой вы точно знаете, что все элементы типа AnyObject на самом деле имеют некоторый определенный тип. В таком случае для анализа элементов типа AnyObject необходимо выполнить приведение типа (листинг 28.4).
Листинг 28.4
for object in someObjects {
let animal = object as! Dog
print(animal.type)
}
Консоль
dog
dog
dog
Чтобы сократить запись, вы можете выполнить приведение типа для преобразования всего массива целиком (листинг 28.5).
Листинг 28.5
for object in someObjects as! [Dog]{
print(animal.type)
}