Урок 3.3 Приведение и Проверка типов

Урок 3.3 Приведение и Проверка типов

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

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


Чему вы научитесь

  • Как смешивать значения разных типов в одной коллекции
  • Как проверять конкретный тип значения в гетерогенной коллекции
  • Как понизить тип объекта до определенного типа перед доступом к его свойствам и методам

Терминология


Связанные ресурсы

Руководство по языку программирования Swift: Приведение типов


Приведение типов

Предположим, что работа Брэда заключается в посещении домов его клиентов и уходе за их питомцами. Когда он прибывает на каждое место, он выполняет различные задачи в зависимости от типа животного. Если питомец — это собака, он выгуливает ее. Если это кошка, он меняет кошачий лоток. А если это птица, он чистит клетку.

В Swift объявление функции определяет тип данных, которые будут возвращены. Поскольку функция не может вернуть одновременно Dog, Cat и Bird, лучшее, что она может сделать, — это вернуть родительский тип для всех трех, Animal.

 

func getClientPet() -> Animal {

   // возвращает питомца

}

let pet = getClientPet() // pet имеет тип Animal

 

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

func walk(dog: Dog) {
  print(”Выгул \(dog.name)”)
}
 
func cleanLitterBox(cat: Cat) {
  print(”Чистка \(cat.boxSize) туалета”)
}
func cleanCage(bird: Bird) {
  print(”Удаление \(bird.featherColor) перьев на дне клетки)

 

Брэду нужно иметь возможность получить версию pet (питомца), которая является одним из подклассов Animal. Вы можете использовать оператор as?, чтобы попытаться понизить тип значения до более конкретного типа и сохранить его в новой константе. Эта операция известна как условное приведение (conditional cast), потому что она приводит экземпляр к указанному типу, если это возможно. Используйте синтаксис if-let, чтобы проверить условия перед преобразованием типа:

 

let pets = allClientAnimals()
 
for pet in pets {
if let dog = pet as? Dog {
    walk(dog: dog)
  } else if let cat = pet as? Cat {
    cleanLitterBox(cat: cat)
  } else if let bird = pet as? Bird {
    cleanCage(bird: bird)
  }
}

 

Теперь Брэд может быть уверен, что он выгуливает собак, чистит кошачьи лотки и чистит клетки птиц.

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

Представьте, что у Алана есть один питомец, собака, и он идет забирать ее из зоомагазина. Функция fetchPet(for customer: String) может возвращать тип Animal.

 

let alansDog = fetchPet(for: “Alan”)
// alansDog интерпретируется как тип `Animal`

 

Когда вы знаете, что возвращаемый объект будет более конкретного типа, вы можете использовать оператор as! для немедленного приведения значения.

 

let alansDog = fetchPet(for: “Alan”) as! Dog
// alansDog интерпретируется как тип `Dog`

 

Когда вы начнете создавать приложения, вы обнаружите, что при работе с UIKit API могут возвращать очень обобщенные объекты, такие как UIViewController. Но, как разработчик своего приложения, вы знаете, каким должен быть конкретный тип. Например, если нажатие кнопки на представлении FirstViewController всегда вызывает SecondViewController, вы можете принудительно привести destination к SecondViewController в функции prepare(for:sender:). Эта функция вызывается всякий раз, когда вы представляете новый контроллер представления, используя переходы в storyboard.

 

class SecondViewController: UIViewController {
  var names: [String]?
}
 
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  let secondVC = segue.destination as! SecondViewController
  secondVC.names = [”Peter”, “Jamie”, “Tricia”]
}

 

Используйте as! только тогда, когда вы уверены, что конкретный тип правильный.

 

Any

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

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

Вот пример массива, который может содержать экземпляры любого типа, или [Any]:

 

var items: [Any] = [5, "Bill", 6.7, Dog()]

 

Поскольку приведенный выше массив может включать что угодно, нет способа гарантировать тип любого элемента. Например, если вы используете items[0] для доступа к первому элементу в массиве, это вернет значение 5 с неопределенным типом Any. Чтобы использовать значение firstItem как Int, вы бы использовали оператор as?:

 

var items: [Any] = [5, "Bill", 6.7, true]
if let firstItem = items[0] as? Int {
  print(firstItem + 4) // 9
}

 

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

Лабораторная работа

Откройте и завершите упражнения в Lab - Type Casting.playground.

 


Отрывок из книги
Develop in Swift Fundamentals
Apple Education
https://books.apple.com/ru/book/develop-in-swift-fundamentals/id1581182804

 

Information

Apple, the Apple logo, Apple Books, Apple TV, Apple Watch, Cocoa, Cocoa Touch, Finder, Handoff, HealthKit, iPad, iPad Pro, iPhone, iPod touch, Keynote, Mac, macOS, Numbers, Objective-C, Pages, Photo Booth, Safari, Siri, Spotlight, Swift, tvOS, watchOS, and Xcode are trademarks of Apple Inc., registered in the U.S. and other countries. App Store and iBooks Store are service marks of Apple Inc., registered in the U.S. and other countries. ​
The Bluetooth® word mark and logos are registered trademarks owned by Bluetooth SIG, Inc. and any use of such marks by Apple is under license. ​
IOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license. ​
Other product and company names mentioned herein may be trademarks of their respective companies.