Принудительное приведение с помощью as! и его риски
Используйте as! только если успешное приведение гарантировано.
«Принудительное приведение с помощью as! и его риски» — бесплатный урок Swift Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
Принудительное приведение
Оператор принудительного приведения as! за один шаг выполняет понижающее приведение и распаковку. Он сразу возвращает необязательный тип, но вызывает сбой, если приведение не удаётся.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
let dog = a as! Dog
print(dog.bark())Без обёртки опционального значения
В отличие от as?, результат as! имеет обычный тип, а не опциональный. Распаковывать его не нужно.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
let dog: Dog = a as! Dog
print(type(of: dog))Риск использования as!
Если экземпляр на самом деле не имеет целевого типа, as! вызывает аварийное завершение во время выполнения и останавливает программу. Восстановиться после этого нельзя.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
// let cat = a as! Cat // would crash at runtime
print("Only force-cast when certain")Когда принудительное приведение безопасно
Используйте as! только при полной уверенности в типе, например сразу после проверки с помощью is.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
if a is Dog {
let dog = a as! Dog
print(dog.bark())
}Принудительное приведение после гарантии
Если логика программы гарантирует тип, принудительное приведение явно фиксирует эту уверенность. Но если вы не уверены, всё равно отдавайте предпочтение as?.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
func barkIfDog(_ a: Animal) {
guard a is Dog else { return }
let dog = a as! Dog
print(dog.bark())
}
barkIfDog(Dog(name: "Rex"))Сравнение as? и as!
as? безопасен и возвращает опциональное значение, а as! выполняет принудительное приведение и вызывает сбой при неудаче. Выбирайте оператор в зависимости от степени уверенности.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
let safe = a as? Dog
let forced = a as! Dog
print(safe?.bark() ?? "nil")
print(forced.bark())Принудительное приведение коллекций
Принудительное приведение работает и для целых коллекций, например array as! [Dog], но риск сбоя распространяется на каждый элемент.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let animals: [Animal] = [Dog(name: "Rex"), Dog(name: "Buddy")]
let dogs = animals as! [Dog]
for d in dogs { print(d.bark()) }Опасный шаблон
Принудительное приведение массива, содержащего неподходящий элемент, вызывает сбой. Поэтому для смешанных коллекций следует использовать as? отдельно для каждого элемента.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let animals: [Animal] = [Dog(name: "Rex"), Cat(name: "Mia")]
// let dogs = animals as! [Dog] // would crash on the Cat
for a in animals {
if let d = a as? Dog { print(d.bark()) }
}Принудительная распаковка и принудительное приведение
Символ ! в as! по смыслу похож на принудительную распаковку опционального значения: это удобно, но опасно, если предположение окажется неверным.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
let viaOptional = (a as? Dog)!
let viaForce = a as! Dog
print(viaOptional.name, viaForce.name)Рекомендации
По умолчанию используйте as?. Оставляйте as! для случаев, когда неудача означает ошибку программиста, которую нужно немедленно обнаружить во время разработки.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
func process(_ a: Animal) -> String {
if let d = a as? Dog { return d.bark() }
return "not a dog"
}
print(process(Dog(name: "Rex")))
print(process(Cat(name: "Mia")))Пример принудительного приведения
Если тип проверен, as! выглядит лаконично и понятно. Если вы сомневаетесь, используйте безопасное условное приведение.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let known: Animal = Dog(name: "Rex")
if known is Dog {
print((known as! Dog).bark())
}Быстрая проверка
Рассмотрите опасность принудительного приведения.
Итоги: принудительное приведение и его риски
Вы узнали, что as! выполняет понижающее приведение и распаковывает результат до необязательного типа, но вызывает сбой при неудаче, что его следует использовать только при уверенности в типе, часто после проверки с помощью is, а as? остаётся более безопасным вариантом по умолчанию.
class Animal {
let name: String
init(name: String) { self.name = name }
}
class Dog: Animal {
func bark() -> String { return name + " says Woof" }
}
class Cat: Animal {
func meow() -> String { return name + " says Meow" }
}
let a: Animal = Dog(name: "Rex")
if a is Dog { print((a as! Dog).bark()) } else { print("safe skip") }Часто задаваемые вопросы
Урок «Принудительное приведение с помощью as! и его риски» бесплатный?
Да — полный текст урока «Принудительное приведение с помощью as! и его риски» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «Принудительное приведение с помощью as! и его риски»?
Используйте as! только если успешное приведение гарантировано. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Принудительное приведение с помощью as! и его риски»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы повышающего и понижающего приведения
- Условное приведение с помощью as?
- Принудительное приведение с помощью as! и его риски
- Проверка типов с помощью is