0Pricing
Swift Academy · Урок

Принудительное приведение с помощью 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 — локальная установка не требуется.

Все уроки этого курса

  1. Основы повышающего и понижающего приведения
  2. Условное приведение с помощью as?
  3. Принудительное приведение с помощью as! и его риски
  4. Проверка типов с помощью is
← Назад к Swift Academy