Swift Language
Обработка ошибок
Поиск…
замечания
Дополнительные сведения об ошибках см. В разделе Быстрый язык программирования .
Основы обработки ошибок
Функции в Swift могут возвращать значения, бросать ошибки или и то, и другое:
func reticulateSplines() // no return value and no error
func reticulateSplines() -> Int // always returns a value
func reticulateSplines() throws // no return value, but may throw an error
func reticulateSplines() throws -> Int // may either return a value or throw an error
Любое значение, которое соответствует протоколу ErrorType (включая объекты NSError), может быть выбрано как ошибка. Перечисления обеспечивают удобный способ определения пользовательских ошибок:
enum NetworkError: ErrorType {
case Offline
case ServerError(String)
}
enum NetworkError: Error {
// Swift 3 dictates that enum cases should be `lowerCamelCase`
case offline
case serverError(String)
}
Ошибка указывает на нефатальный сбой во время выполнения программы и обрабатывается специализированными конструкциями потока управления do
/ catch
, throw
и try
.
func fetchResource(resource: NSURL) throws -> String {
if let (statusCode, responseString) = /* ...from elsewhere...*/ {
if case 500..<600 = statusCode {
throw NetworkError.serverError(responseString)
} else {
return responseString
}
} else {
throw NetworkError.offline
}
}
Ошибки могут быть обнаружены с помощью do
/ catch
:
do {
let response = try fetchResource(resURL)
// If fetchResource() didn't throw an error, execution continues here:
print("Got response: \(response)")
...
} catch {
// If an error is thrown, we can handle it here.
print("Whoops, couldn't fetch resource: \(error)")
}
Любая функция, которая может вызвать ошибку, должна вызываться с помощью try
, try?
, или try!
:
// error: call can throw but is not marked with 'try'
let response = fetchResource(resURL)
// "try" works within do/catch, or within another throwing function:
do {
let response = try fetchResource(resURL)
} catch {
// Handle the error
}
func foo() throws {
// If an error is thrown, continue passing it up to the caller.
let response = try fetchResource(resURL)
}
// "try?" wraps the function's return value in an Optional (nil if an error was thrown).
if let response = try? fetchResource(resURL) {
// no error was thrown
}
// "try!" crashes the program at runtime if an error occurs.
let response = try! fetchResource(resURL)
Поиск различных типов ошибок
Давайте создадим наш собственный тип ошибки для этого примера.
enum CustomError: ErrorType {
case SomeError
case AnotherError
}
func throwing() throws {
throw CustomError.SomeError
}
enum CustomError: Error {
case someError
case anotherError
}
func throwing() throws {
throw CustomError.someError
}
Синтаксис Do-Catch позволяет поймать заброшенную ошибку и автоматически создает постоянную именованную error
доступную в блоке catch
:
do {
try throwing()
} catch {
print(error)
}
Вы также можете объявить переменную самостоятельно:
do {
try throwing()
} catch let oops {
print(oops)
}
Это также возможно цепь различных catch
отчетности. Это удобно, если в блоке Do можно выбросить несколько типов ошибок.
Здесь Do-Catch сначала попытается передать ошибку как CustomError
, а затем как NSError
если пользовательский тип не был сопоставлен.
do {
try somethingMayThrow()
} catch let custom as CustomError {
print(custom)
} catch let error as NSError {
print(error)
}
В Swift 3 нет необходимости явно отключать NSError.
do {
try somethingMayThrow()
} catch let custom as CustomError {
print(custom)
} catch {
print(error)
}
Схема улова и переключения для явной обработки ошибок
class Plane {
enum Emergency: ErrorType {
case NoFuel
case EngineFailure(reason: String)
case DamagedWing
}
var fuelInKilograms: Int
//... init and other methods not shown
func fly() throws {
// ...
if fuelInKilograms <= 0 {
// uh oh...
throw Emergency.NoFuel
}
}
}
В классе клиента:
let airforceOne = Plane()
do {
try airforceOne.fly()
} catch let emergency as Plane.Emergency {
switch emergency {
case .NoFuel:
// call nearest airport for emergency landing
case .EngineFailure(let reason):
print(reason) // let the mechanic know the reason
case .DamagedWing:
// Assess the damage and determine if the president can make it
}
}
Отключение распространения ошибок
Создатели Swift уделяют много внимания тому, чтобы сделать язык выразительным, а обработка ошибок - это именно то, что выразительно. Если вы попытаетесь вызвать функцию, которая может выдать ошибку, вызов функции должен предшествовать ключевому слову try. Ключевое слово try не является волшебным. Все, что он делает, делает разработчик осведомленным о способности бросать функцию.
Например, следующий код использует функцию loadImage (atPath :), которая загружает ресурс изображения по заданному пути или выдает ошибку, если изображение не может быть загружено. В этом случае, поскольку изображение поставляется вместе с приложением, во время выполнения не будет выдаваться ошибка, поэтому целесообразно отключить распространение ошибок.
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
Создание пользовательской ошибки с локализованным описанием
Создать enum
пользовательских ошибок
enum RegistrationError: Error {
case invalidEmail
case invalidPassword
case invalidPhoneNumber
}
Создайте extension
RegistrationError
для обработки описания Localized.
extension RegistrationError: LocalizedError {
public var errorDescription: String? {
switch self {
case .invalidEmail:
return NSLocalizedString("Description of invalid email address", comment: "Invalid Email")
case .invalidPassword:
return NSLocalizedString("Description of invalid password", comment: "Invalid Password")
case .invalidPhoneNumber:
return NSLocalizedString("Description of invalid phoneNumber", comment: "Invalid Phone Number")
}
}
}
Ошибка обработки:
let error: Error = RegistrationError.invalidEmail
print(error.localizedDescription)