Проваливающиеся инициализаторы

Failable Initializers

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

NSImage(contentsOfFile: "swift.png")

Если файл не существует или не читается по какой-либо причине, инициализация NSImage потерпит неудачу. С Swift версии 1.1, такие сбои могут быть представлены в виде проваливающегося инициализатора. При построении объекта с использованием проваливающегося инициализатора, результатом является опционал, который, либо содержит объект (если инициализация удалась), либо содержит nil (если инициализация не удалась). Таким образом, инициализация, рассмотренная выше, должна обрабатывать результат опционала напрямую:

  1. if let image = NSImage(contentsOfFile: "swift.png") {
  2.  // успешно загружает изображение
  3. } else {
  4.  // не может загрузить изображение
  5. }

Инициализатор объявленный через init можно сделать проваливающимся, добавив ? или ! после init, что указывает на форму опционала, который будет получаться путем построения объекта с помощью этого инициализатора. Например, можно было бы добавить проваливающийся инициализатор к Int, который пытается выполнить преобразование из String:

  1. extension Int {
  2.  init?(fromString: String) {
  3.   if let i = fromString.toInt() {
  4.    // Инициализация
  5.    self = i
  6.   } else {
  7.    // возвращает nil, отбрасывание self подразумевается
  8.    return nil
  9.   }
  10.  }
  11. }

В проваливающемся инициализаторе, return nil означает, что инициализация не удалась и никакое другое значение не может быть возвращено. В примере, сбой происходит, когда строка не может быть проанализирована в целое число. В противном случае, self инициализирует преобразованное значение.

Проваливающиеся инициализаторы устраняют наиболее распространенные причины фабричных методов в Swift, которые ранее были единственным способом сообщить о неудаче при построении этого объекта. Например, перечисления, имеющие raw тип имеют фабричный метод fromRaw, который возвращает опциональный enum. Теперь, Swift компилятор синтезирует проваливающийся инициализатор, который принимает raw значение и пытается сопоставить его с одним из case'ов перечисления. Например:

  1. enum Color : Int {
  2.  case Red = 0, Green = 1, Blue = 2
  3.  
  4.  // неявно синтезирован
  5.  var rawValue: Int { /* возвращает raw значение текущего case */ }
  6.  
  7.  // неявно синтезирован
  8.  init?(rawValue: Int) {
  9.   switch rawValue {
  10.    case 0: self = .Red
  11.    case 1: self = .Green
  12.    case 2: self = .Blue
  13.    default: return nil
  14.   }
  15.  }
  16. }

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