Синтаксис Swift do-try-catch
Я даю ему попробовать понять новую вещь обработки ошибок в swift 2. Вот что я сделал: я сначала объявил об ошибке перечисление:
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 перечисление.
для регулярных операторов SWIFT может понять, что он является исчерпывающим, когда каждый случай обрабатывается.
5 ответов:
в модели обработки ошибок Swift 2 есть два важных момента: исчерпываемость и отказоустойчивость. Вместе они сводятся к вашему
do/catchзаявление необходимо поймать все возможные ошибки, а не только те, которые вы знаете, вы можете бросить.обратите внимание, что вы не объявляете, какие типы ошибок функция может бросить, только ли она бросает вообще. Это проблема типа ноль-один-бесконечность: как кто-то определяет функцию для других (включая ваше будущее я), чтобы использовать, вы не хочу, чтобы каждый клиент вашей функции адаптировался к каждому изменению в реализации вашей функции, в том числе к тому, какие ошибки он может выбросить. Вы хотите, чтобы код, который вызывает вашу функцию, был устойчив к таким изменениям.
потому что ваша функция не может сказать, какие ошибки он бросает (или может бросить в будущем),
catchблоки, которые ловят его ошибки не знаю, какие типы ошибок он может бросить. Таким образом, в дополнение к обработке типов ошибок, о которых вы знаете, вам нужно обрабатывать те, которые вы не с универсальнымcatchоператор -- таким образом, если ваша функция изменяет набор ошибок, которые она выдает в будущем, вызывающие по-прежнему будут ловить свои ошибки.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") } catch let error { print(error.localizedDescription) }но давайте не будем останавливаться на достигнутом. Подумайте об этой идее устойчивости еще немного. То, как вы разработали свой сэндвич, вы должны описать ошибки в каждом месте, где вы их используете. Это означает, что всякий раз, когда вы меняете набор случаев ошибок, вы должны изменить каждое место, которое их использует... не очень веселье.
идея определения собственных типов ошибок заключается в том, чтобы позволить вам централизовать такие вещи. Вы могли бы определить
descriptionметод для ваших ошибок:extension SandwichError: CustomStringConvertible { var description: String { switch self { case NotMe: return "Not me error" case DoItYourself: return "Try sudo" } } }и тогда ваш код обработки ошибок может попросить ваш тип ошибки описать себя - теперь каждое место, где вы обрабатываете ошибки, может использовать тот же код и обрабатывать возможные будущие случаи ошибок.
do { let sandwich = try makeMeSandwich(kitchen) print("i eat it \(sandwich)") } catch let error as SandwichError { print(error.description) } catch { print("i dunno") }это также прокладывает путь для типов ошибок (или расширениями на них) поддержка других способов сообщения об ошибках - например, у вас может быть расширение для вашего типа ошибок, которое знает, как представить
UIAlertControllerдля сообщения об ошибке пользователю iOS.
Я подозреваю, что это просто еще не реализована должным образом. Элемент Swift Руководство По Программированию определенно, похоже, подразумевается, что компилятор может выводить исчерпывающие соответствия "как оператор switch". Он не упоминает о необходимости генерала
catchчтобы быть исчерпывающим.вы также заметите, что ошибка на
tryстрока, а не конец блока, т. е. в какой-то момент компилятор сможет определить, какойtryоператор в блоке имеет необрабатываемое исключение.документация немного неоднозначна, хотя. Я просмотрел видео "Что нового в Swift" и не смог найти никаких подсказок; я буду продолжать пытаться.
обновление:
теперь мы до бета-версии 3 без намека на вывод типа ErrorType. Теперь я считаю, что если это когда-либо планировалось (и я все еще думаю, что это было в какой-то момент), динамическая отправка по расширениям протокола, вероятно, убила его.
бета 4 Обновление:
Xcode 7b4 добавлена поддержка комментариев doc для
Throws:, который "должен использоваться для документирования того, какие ошибки могут быть выброшены и почему". Я думаю, это по крайней мере обеспечивает некоторые механизм для передачи ошибок потребителям API. Кому нужна система типов, когда у вас есть документация!другие новости:
проведя некоторое время в надежде на автоматическое
ErrorTypeвывод и разработка того, какие ограничения будут иметь эта модель, я передумал -этой Я надеюсь, что вместо яблока реализует. По существу:// allow us to do this: func myFunction() throws -> Int // or this: func myFunction() throws CustomError -> Int // but not this: func myFunction() throws CustomErrorOne, CustomErrorTwo -> IntОбновление
теперь доступно обоснование обработки ошибок Apple здесь. Также были некоторые интересные обсуждения по swift-evolution список рассылки. По сути, Джон Макколл выступает против типизированных ошибок, потому что он считает, что большинство библиотек в конечном итоге будут включать общий случай ошибки во всяком случае, и эти типизированные ошибки вряд ли добавят много к коду, кроме шаблонного (он использовал термин "желательный блеф"). Крис Латтнер сказал, что он открыт для типизированных ошибок в Swift 3, если он может работать с моделью устойчивости.
Swift беспокоится, что ваш оператор case не охватывает все случаи, чтобы исправить это, вам нужно создать случай по умолчанию:
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") } catch Default { print("Another Error") }
Я также был разочарован отсутствием функции типа A, но теперь я получаю ее благодаря @rickster, и я подытожу ее следующим образом: допустим, мы можем указать тип функции a, у нас будет что-то вроде этого:
enum MyError: ErrorType { case ErrorA, ErrorB } func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... } do { try myFunctionThatThrows() } case .ErrorA { ... } case .ErrorB { ... }проблема в том, что даже если мы ничего не меняем в myFunctionThatThrows, если мы просто добавим случай ошибки в MyError:
enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC }мы облажались, потому что наш do / try / catch больше не является исчерпывающим, а также любым другим место, где мы вызвали функции, которые бросают MyError
создать перечисление следующим образом:
//Error Handling in swift enum spendingError : Error{ case minus case limit }создать метод, как:
func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{ if morningSpending < 0 || eveningSpending < 0{ throw spendingError.minus } if (morningSpending + eveningSpending) > 100{ throw spendingError.limit } return morningSpending + eveningSpending }Теперь проверьте ошибка есть или нет и обрабатывать его:
do{ try calculateSpending(morningSpending: 60, eveningSpending: 50) } catch spendingError.minus{ print("This is not possible...") } catch spendingError.limit{ print("Limit reached...") }
Comments