Я попытаюсь понять новую вещь по обработке ошибок в swift 2. Вот что я сделал: я сначала объявил enum:
enum SandwichError: ErrorType {
case NotMe
case DoItYourself
}
А потом я объявил метод, который выдает ошибку (не исключение, ребята. Это ошибка.). Вот этот метод:
func makeMeSandwich(names: [String: String]) throws -> String {
guard let sandwich = names["sandwich"] else {
throw SandwichError.NotMe
}
return sandwich
}
Проблема с вызывающей стороны. Вот код, который вызывает этот метод:
let kitchen = ["sandwich": "ready", "breakfeast": "not ready"]
do {
let sandwich = try makeMeSandwich(kitchen)
print("i eat it \(sandwich)")
} catch SandwichError.NotMe {
print("Not me error")
} catch SandwichError.DoItYourself {
print("do it error")
}
После do
строки компилятор говорит Errors thrown from here are not handled because the enclosing catch is not exhaustive
. Но, на мой взгляд, это исчерпывающее, потому что в SandwichError
enum есть только два случая .
Для регулярных операторов переключения swift может понять, что он исчерпывающий, когда обрабатывается каждый случай.
do
блоки на верхнем уровне, которые не являются исчерпывающими - если вы оберните do в функцию без броска, это вызовет ошибку.Ответы:
У модели обработки ошибок Swift 2 есть два важных момента: полнота и отказоустойчивость. Вместе они сводятся к вашему
do
/catch
заявлению необходимости ловить все возможные ошибки, а не только те , которые вы знаете , что вы можете бросить.Обратите внимание, что вы не объявляете, какие типы ошибок может генерировать функция, а только выдает ли она вообще. Это проблема типа ноль-одна-бесконечность: поскольку кто-то определяет функцию, которую будут использовать другие (в том числе и ваше будущее), вам не нужно заставлять каждого клиента вашей функции приспосабливаться к каждому изменению в реализации вашей программы. функция, в том числе какие ошибки он может выдать. Вы хотите, чтобы код, вызывающий вашу функцию, был устойчивым к таким изменениям.
Поскольку ваша функция не может сказать, какие ошибки она выдает (или может выдать в будущем),
catch
блоки, которые ее отлавливают, не знают, какие типы ошибок она может генерировать. Таким образом, в дополнение к обработке типов ошибок, о которых вы знаете, вам нужно обрабатывать те, которые вы не делаете, с помощью универсальногоcatch
оператора - таким образом, если ваша функция изменит набор ошибок, которые она выдает в будущем, вызывающие по-прежнему будут ловить ее ошибки.Но давайте не будем останавливаться на достигнутом. Подумайте еще об этой идее устойчивости. Как вы спроектировали свой бутерброд, вы должны описывать ошибки в каждом месте, где вы их используете. Это означает, что всякий раз, когда вы меняете набор ошибок, вы должны менять каждое место, где они используются ... не очень весело.
Идея определения ваших собственных типов ошибок состоит в том, чтобы позволить вам централизовать подобные вещи. Вы можете определить
description
метод для ваших ошибок:И тогда ваш код обработки ошибок может попросить ваш тип ошибки описать себя - теперь каждое место, где вы обрабатываете ошибки, может использовать один и тот же код, а также обрабатывать возможные будущие случаи ошибок.
Это также прокладывает путь для типов ошибок (или расширений для них) для поддержки других способов сообщения об ошибках - например, у вас может быть расширение для вашего типа ошибки, которое знает, как представить
UIAlertController
сообщение об ошибке для пользователя iOS.источник
error caught in main()
. Так что, хотя все, что вы сказали, звучит разумно, я не могу воспроизвести это поведение.try
выражения в производственном коде, так как это может вызвать ошибку времени выполнения и привести к сбою приложенияtry!
. Также, возможно, существуют допустимые, «безопасные» варианты использования для различных «принудительных» операций в Swift (разворачивание, попытка и т. Д.) Даже для производственного кода - если с помощью предусловия или конфигурации вы надежно исключили возможность сбоя, это может более разумно закорачивать на мгновенный сбой, чем писать код обработки ошибок, который невозможно проверить.SandwichError
класса имеет смысл. Однако, я подозреваю, что для большинства ошибок логика обработки ошибок не может быть настолько инкапсулирована. Это связано с тем, что обычно требуется знание контекста вызывающего абонента (либо восстановить, либо повторить попытку, либо сообщить о сбое в восходящем направлении и т. Д.). Другими словами, я подозреваю, что наиболее распространенным шаблоном должно быть сопоставление с конкретными типами ошибок.Я подозреваю, что это просто еще не реализовано должным образом. Руководство по программированию Swift определенно подразумевает, что компилятор может выводить исчерпывающие совпадения «как оператор switch». В нем не упоминается необходимость генерала
catch
, чтобы быть исчерпывающим.Вы также заметите, что ошибка находится в
try
строке, а не в конце блока, то есть в какой-то момент компилятор сможет определить, какойtry
оператор в блоке имеет необработанные типы исключений.Документация немного двусмысленная, хотя. Я пролистал видео «Что нового в Swift» и не смог найти никаких подсказок; Я буду продолжать пытаться
Обновить:
Теперь мы подошли к бета-версии 3 без намека на вывод ErrorType. Теперь я верю, что если это когда-либо планировалось (и я все еще думаю, что это было в какой-то момент), динамическая диспетчеризация расширений протокола, вероятно, убила его.
Бета 4 Обновление:
Xcode 7b4 добавил поддержку комментариев к документу
Throws:
, который «должен использоваться для документирования, какие ошибки могут быть сгенерированы и почему». Я предполагаю, что это по крайней мере обеспечивает некоторый механизм для сообщения об ошибках потребителям API. Кому нужна система типов, когда у вас есть документация!Еще одно обновление:
Потратив некоторое время в надежде на автоматический
ErrorType
вывод и выяснив, какими будут ограничения для этой модели, я передумал - это то, что я надеюсь, Apple реализует вместо этого. По существу:Еще одно обновление
Обоснование обработки ошибок Apple теперь доступно здесь . Были также некоторые интересные обсуждения в списке рассылки swift-evolution . По сути, Джон МакКолл против типизированных ошибок, потому что он считает, что большинство библиотек в конечном итоге будет содержать общий случай ошибок, и что типизированные ошибки вряд ли добавят много кода, кроме шаблонного (он использовал термин «желательный блеф»). Крис Латтнер сказал, что он открыт для опечаток в Swift 3, если он может работать с моделью устойчивости.
источник
Свифт обеспокоен тем, что ваше заявление о кейсе не охватывает все случаи, чтобы его исправить, вам нужно создать случай по умолчанию:
источник
catch
заявлениях.func method() throws(YourErrorEnum)
или дажеthrows(YourEnum.Error1, .Error2, .Error3)
знает, что может быть брошеноЯ также был разочарован отсутствием типа, который может выдавать функция, но теперь я получаю его благодаря @rickster, и я резюмирую это следующим образом: допустим, мы могли бы указать тип, который выдает функция, у нас будет что-то вроде этого:
Проблема в том, что даже если мы ничего не изменим в myFunctionThatThrows, просто добавим случай ошибки в MyError:
мы облажались, потому что наш do / try / catch больше не является исчерпывающим, как и любое другое место, где мы вызывали функции, которые выбрасывают MyError
источник
catch {}
в нижней части каждого блока, возможно, еще хуже. Я надеюсь, что компилятор в конечном итоге выведет типы ошибок автоматически, но не смог подтвердить.Теперь подтвердите номер:
источник
Создайте перечисление следующим образом:
Создайте метод как:
Теперь проверьте, есть ошибка или нет, и обработайте ее:
источник