Организуйте свои ошибки | Кодементор

23 апр 2017
(эта статья изначально была написана для моего блога vojtastavik.com)

Есть два типа программистов: те, кто правильно обрабатывает ошибки, и те, с которыми вы не хотите работать.

Это происходит снова и снова. Что-то перестало работать, и мне нужно это исправить. После десятков минут отладки и более глубокого погружения в кодовую базу я наконец нашел его. Кто-то (включая меня несколько лет назад) проигнорировал ошибку и оставил ее необработанной.

Причина, по которой эта ситуация так раздражает, проста. Информация об ошибке, обычно включая причину возникновения ошибки, была тут же! Кто-то просто решил не обращать на это внимания.

Правильная обработка ошибок: старые (Objective-C) времена

старые времена

API-интерфейсы Objective-C имеют довольно неясный способ, как сигнализировать об ошибках. Брать NSData и это writeToFile функция в качестве примера:

- (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)writeOptionsMask error:(NSError **)errorPtr;

Функция возвращает BOOL значение, указывающее, была ли операция успешной. Когда значение NOвы можете найти дополнительную информацию об ошибке внутри объекта, на который ссылается errorPtr. Заметь erorrPtr это указатель на указатель на NSError объект.

NSData* data = [NSData new]; // some data
NSString* path = @"path"; // some path // ⚠️ DO NOT DO THIS ⚠️ 👎
// This code completely ignores the error and it will, sooner or later,
// bite you back (or ever worse: bite your colleagues).
[data writeToFile:path options:0 error:nil]; // YOU SHOULDN'T EVEN DO THIS 👎
// This is definitely better than the previous. However, you need 
// to check the returning value, not just the error pointer. 
// Some APIs could return non-nil error even for non-error states.
NSError* error1;
[data writeToFile:path options:0 error:&error1];
if (error1) { // Do something with the error. If nothing, at least log it // to the console.
} // THE PROPER WAY 👍
NSError* error2;
if ([data writeToFile:path options:0 error:&error2]) { // Happy path
} else { // Here we're sure something went wrong if (error2) { // We even have more info about it }
}

Правильная обработка ошибок: Swift

Несмотря на то, что в Swift 2 обработка ошибок стала проще и понятнее, все еще есть много способов сделать это неправильно.

let data = Data() // Some data
let url = URL(string: "url")! // Some URL // ⚠️ DO NOT DO THIS ⚠️ 👎
// Using try! will crash your app
// when an error occurs. Like implicitly
// unwrapped optionals - use try!
// only if you really know what you
// are doing (i.e. in your tests).
try! data.write(to: url) // ⚠️ DO NOT DO THIS ⚠️ 👎
// This particular usage of try?
// is also not good. When the write
// function fails, the error is
// silently consumed and no one
// will ever know about it.
try? data.write(to: url) // YOU CAN DO THIS 👍
// If the return value of the throwing
// function is what matters, it might be
// a good idea to use try?. You won't 
// have any further information about 
// the error, though.
guard let data = try? Data(contentsOf: url) else { // Something went wrong, but we don't why return
}
// Happy path: Everything is OK and we can use data
print(data.description) // BASIC ERROR HANDLING 👍
do { try data.write(to: url)
} catch { // You can access variable 'error' of // the type 'Error' within this scope.
}

Вы не можете получить ошибку времени выполнения

Пользовательские типы ошибок в Swift

Здесь начинается самое интересное. Вы можете определить свои собственные типы ошибок и сделать так, чтобы ваши функции их выдавали:

enum Error: Swift.Error { case network(NetworkError) enum NetworkError { case notReachable case unknown } case device(DeviceError) enum DeviceError { case notEnoughSpace case unsupportedSystemVersion }
}

Затем вы можете обрабатывать определенные ошибки иначе, чем другие:

// COMPLETE ERROR HANDLING 👍
// (WITH CUSTOM ERRORS) do { try doSomething() } catch let Error.network(error) { // The returned error is a network error. print(type(of: error)) // prints `Error.NetworkError` // ... do something with the error (log it, etc.) } catch let Error.device(error) { // The returned error is a device error. print(type(of: error)) // prints `Error.DeviceError` // ... do something different with the error (show alert etc.) } catch { // Some other error without specific handling.
}

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *