Обработка ошибок в Swift-Language

190

Я не слишком много читал о Swift, но заметил одну вещь: исключений нет. Так как же они обрабатывают ошибки в Swift? Кто-нибудь нашел что-нибудь связанное с обработкой ошибок?

PEKO
источник
1
Я нашел сообщения об ошибках, как и в Obj-C: o
Arbitur
13
@ Арбитур старый добрый путь Сегфо?
peko
Создал NSTimer в Swift, и когда я неправильно написал функцию, она упала и вызвала ошибку, сообщив, что не может найти метод :)
Arbitur
3
Вы можете добавить поддержку try-catch для Swift, следуя инструкциям в этой статье: medium.com/@_willfalcon/adding-try-catch-to-swift-71ab27bcb5b8
Уильям Фалькон,
@peko Как вы справляетесь с segfault в Swift? Я не думаю, что это возможно на данный момент, что, к сожалению, делает некоторые ошибки неустранимыми
Орлин Георгиев

Ответы:

148

Свифт 2 и 3

Ситуация немного изменилась в Swift 2, поскольку появился новый механизм обработки ошибок, который несколько больше похож на исключения, но отличается в деталях.

1. Указание на возможность ошибки

Если функция / метод хочет указать, что она может выдать ошибку, она должна содержать throwsключевое слово, подобное этому

func summonDefaultDragon() throws -> Dragon

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

2. Вызывающая функция, которая может выдавать ошибки

Для вызова функции вам нужно использовать ключевое слово try, например:

try summonDefaultDragon()

в этой строке обычно должен присутствовать блок do-catch, подобный этому

do {
    let dragon = try summonDefaultDragon() 
} catch DragonError.dragonIsMissing {
    // Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
    // Other specific-case error-handlng
} catch {
    // Catch all error-handling
}

Примечание: предложение catch использует все мощные функции сопоставления с образцом Swift, поэтому вы здесь очень гибки.

Вы можете решить распространить ошибку, если вы вызываете функцию выброса из функции, которая сама помечена throwsключевым словом:

func fulfill(quest: Quest) throws {
    let dragon = try summonDefaultDragon()
    quest.ride(dragon)
} 

Кроме того, вы можете вызвать функцию броска, используя try?:

let dragonOrNil = try? summonDefaultDragon()

Таким образом, вы получите либо возвращаемое значение, либо nil, если произошла какая-либо ошибка. Используя этот способ, вы не получите объект ошибки.

Это означает, что вы также можете сочетать try?с полезными утверждениями, такими как:

if let dragon = try? summonDefaultDragon()

или

guard let dragon = try? summonDefaultDragon() else { ... }

Наконец, вы можете решить, что знаете, что ошибка на самом деле не возникнет (например, потому что вы уже проверили предварительные условия), и использовать try!ключевое слово:

let dragon = try! summonDefaultDragon()

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

3. Бросать ошибку

Чтобы сгенерировать ошибку, используйте ключевое слово throw, как это

throw DragonError.dragonIsMissing

Вы можете бросить все, что соответствует ErrorTypeпротоколу. Для начала NSErrorсоответствует этому протоколу, но вы, вероятно, хотели бы использовать enum-based, ErrorTypeкоторый позволяет группировать несколько связанных ошибок, возможно, с дополнительными фрагментами данных, например

enum DragonError: ErrorType {
    case dragonIsMissing
    case notEnoughMana(requiredMana: Int)
    ...
}

Основные различия между новым механизмом ошибок Swift 2 и 3 и исключениями в стиле Java / C # / C ++ заключаются в следующем:

  • Синтаксис немного отличается: do-catch+ try+ deferпротив традиционного try-catch-finallyсинтаксиса.
  • Обработка исключений обычно приводит к гораздо большему времени выполнения в пути исключения, чем в пути успеха. Это не относится к ошибкам Swift 2.0, где путь успеха и путь ошибки примерно одинаковы.
  • Весь код сброса ошибок должен быть объявлен, в то время как исключения могли быть выброшены откуда угодно. Все ошибки являются «проверенными исключениями» в номенклатуре Java. Однако, в отличие от Java, вы не указываете потенциально выбрасываемые ошибки.
  • Исключения Swift не совместимы с исключениями ObjC. Ваш do-catchблок не будет перехватывать исключение NSEx и наоборот, для этого вы должны использовать ObjC.
  • Исключения Swift совместимы с NSErrorсоглашениями метода Какао о возвращении либо false(для Boolвозврата функций), либо nil(для AnyObjectвозврата функций) и передачи NSErrorPointerс подробностями ошибки.

В качестве дополнительного синтетического сахара для облегчения обработки ошибок, есть еще две концепции

  • отложенные действия (с использованием deferключевого слова), которые позволяют достичь того же эффекта, что и блоки finally в Java / C # / etc
  • Охранный оператор (с использованием guardключевого слова), который позволяет писать немного меньше кода if / else, чем в обычном коде проверки / сигнализации ошибок

Свифт 1

Ошибки во время выполнения:

Как Leandros предлагает для обработки ошибок времени выполнения (таких как проблемы с сетевым подключением, анализ данных, открытие файла и т. Д.), Вы должны использовать их так же, NSErrorкак в ObjC, потому что Foundation, AppKit, UIKit и т. Д. Сообщают о своих ошибках таким образом. Так что это скорее элементарная вещь, чем языковая.

Другой частый шаблон, который используется, это блоки успеха / отказа разделителя, как в AFNetworking:

var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
    success: { (NSURLSessionDataTask) -> Void in
        println("Success")
    },
    failure:{ (NSURLSessionDataTask, NSError) -> Void in
        println("Failure")
    })

По-прежнему в блоке NSErrorс ошибками часто принимают экземпляр, описывающий ошибку.

Ошибки программиста:

Для ошибок программиста (например, за пределами доступа к элементу массива, неверных аргументов, переданных на вызов функции и т. Д.) Вы использовали исключения в ObjC. Язык Swift, похоже, не имеет никакой языковой поддержки для исключений (например throw, catchключевое слово etc). Тем не менее, как видно из документации, он работает в той же среде выполнения, что и ObjC, и поэтому вы все еще можете выполнить NSExceptionsследующее:

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

Вы просто не можете поймать их в чистом Swift, хотя вы можете выбрать перехват исключений в коде ObjC.

Вопрос заключается в том, следует ли вам выдавать исключения для ошибок программиста, или, скорее, использовать утверждения, как Apple предлагает в руководстве по языку.

MDJ
источник
20
«проблемы с сетевым подключением» и «открытие файлов» с помощью API-интерфейсов Какао (NSFileHandle) могут вызывать исключения, которые необходимо перехватить. Без исключений в Swift, вам нужно реализовать эту часть вашей программы в Objective-C или выполнить всю вашу работу, используя API-интерфейсы BSD C (оба из которых являются плохими обходными путями). См. Документацию для NSFileHandle.writeData для получения дополнительной информации ... developer.apple.com/library/ios/documentation/Cocoa/Reference/… :
Мэтт Галлахер,
5
Опять же, отсутствие обработки исключений означает двухэтапное построение объекта со всеми присущими ему проблемами. Смотрите stroustrup.com/except.pdf .
Фил
2
fatalError(...)такая же , как хорошо.
Holex
8
Как бы мне ни нравился Свифт, я думаю, что это катастрофический выбор, и, почувствовав вкус некоторых последствий, они играют с огнем с этим упущением ...
Роб
2
Да, проверенные исключения теперь используются экономно, потому что было обнаружено, что, заставляя программиста отлавливать исключения, у них мало надежды на восстановление после того, как они загрязняют код, нарушая принцип единой ответственности. Класс уровня домена не хочет иметь дело с исключениями уровня инфраструктуры. Таким образом, теперь непроверенные исключения имеют тенденцию быть предпочтительными, и заинтересованная сторона может при необходимости их перехватить. , Проверено = определенно подлежит восстановлению. Не проверено = не подлежит восстановлению.
Джаспер Блюз
69

Обновление 9 июня 2015 - Очень важно

Swift 2.0 поставляется с try, throwи catchключевыми словами и наиболее интересным является:

Swift автоматически переводит методы Objective-C, которые генерируют ошибки, в методы, которые выдают ошибку в соответствии с собственной функцией обработки ошибок Swift.

Примечание. Методы, которые используют ошибки, такие как методы делегатов или методы, которые принимают обработчик завершения с аргументом объекта NSError, не становятся методами, которые выдают при импорте Swift.

Отрывок из: Apple Inc. «Использование Swift с какао и Objective-C (пререлиз Swift 2)». интерактивные книги.

Пример: (из книги)

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
    NSLog(@"Error: %@", error.domain);
}

Эквивалент по быстрому будет:

let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
    try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
    print ("Error: \(error.domain)")
}

Выдает ошибку:

*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]

Будет автоматически передан вызывающей стороне:

throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)

Из книг Apple, языка программирования Swift, кажется, что ошибки должны обрабатываться с помощью enum.

Вот пример из книги.

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure...  \(error)"
}

От: Apple Inc. «Язык программирования Swift». интерактивные книги. https://itun.es/br/jEUH0.l

Обновить

Из новостных книг Apple «Использование Swift с какао и Objective-C». Исключения во время выполнения не возникают при использовании быстрых языков, поэтому у вас нет try-catch. Вместо этого вы используете опциональную цепочку .

Вот отрывок из книги:

Например, в приведенном ниже листинге кода первая и вторая строки не выполняются, потому что свойство length и characterAtIndex: метод не существуют в объекте NSDate. Константа myLength является необязательным Int и имеет значение nil. Вы также можете использовать оператор if – let для условного развертывания результата метода, на который объект может не отвечать, как показано в строке три

let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
    println("Found \(fifthCharacter) at index 5")
}

Выдержка из: Apple Inc. «Использование Swift с какао и Objective-C». интерактивные книги. https://itun.es/br/1u3-0.l


И книги также побуждают вас использовать образец ошибки какао из Objective-C (NSError Object)

Отчеты об ошибках в Swift следуют той же схеме, что и в Objective-C, с дополнительным преимуществом предложения дополнительных возвращаемых значений. В простейшем случае вы возвращаете значение Bool из функции, чтобы указать, успешно ли оно выполнено. Когда вам нужно сообщить причину ошибки, вы можете добавить в функцию параметр NSError out типа NSErrorPointer. Этот тип примерно эквивалентен NSError ** в Objective-C, с дополнительной безопасностью памяти и необязательной типизацией. Вы можете использовать префикс & оператор для передачи ссылки на необязательный тип NSError в качестве объекта NSErrorPointer, как показано в листинге кода ниже.

var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
    encoding: NSUTF8StringEncoding,
    error: &writeError)
if !written {
    if let error = writeError {
        println("write failure: \(error.localizedDescription)")
    }
}

Выдержка из: Apple Inc. «Использование Swift с какао и Objective-C». интерактивные книги. https://itun.es/br/1u3-0.l

Гильерме Торрес Кастро
источник
Для последнего оператора это должно быть: do {try myString.writeToFile (path, atomically: true, encoding: NSUTF8StringEncoding)} перехватывать ошибку let как NSError {print (error)}
Джеки,
1
@Jacky Да, это верно для swift 2.0, хотя этот ответ был написан до выпуска swift 2.0, я обновил ответ, чтобы показать новый способ обработки ошибок в swift 2.0. Я подумал, позволив этот путь для справки, но я рассмотрю вопрос об обновлении всего ответа, чтобы использовать только Swift 2.0
Гильерме Торрес Кастро
12

В Swift нет исключений, похожих на подход Objective-C.

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

Классический NSErrorподход не меняется, вы отправляете NSErrorPointer, который заполняется.

Краткий пример:

var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
    println("An error occurred \(error)")
} else {
    println("Contents: \(contents)")
}
Leandros
источник
6
Возникают два вопроса: что происходит, когда код ObjC, который мы вызываем из Swift, на самом деле выдает исключение, и является ли NSError нашим универсальным объектом ошибок, как в ObjC?
MDJ
1
Является ли это просто фактом жизни со Swift, что инициализаторы не могут или не могут потерпеть неудачу?
Фил
11
Обработка исключений выглядит довольно грязной
Таш Пемхива
27
Да, кому нужны исключения, когда вы можете просто потерпеть крах? Или положить NSError ** в качестве аргумента во все функции, которые вы объявляете? так что каждый f();g();становится f(&err);if(err) return;g(&err);if(err) return;на первый месяц, потом просто становитсяf(nil);g(nil);hopeToGetHereAlive();
hariseldon78
2
Этот ответ является как устаревшим (Swift теперь поддерживает исключения), так и неправильным (Objective-C поддерживает исключения.
Рог
11

Рекомендуемый «быстрый путь»:

func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
    return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}

var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
    println("write failure 1: \(writeError!.localizedDescription)")
    // assert(false) // Terminate program
}

Однако я предпочитаю попробовать / поймать, так как мне легче следовать, потому что в конце он переносит обработку ошибок в отдельный блок, такое расположение иногда называют «Золотым путем». К счастью, вы можете сделать это с помощью замыканий:

TryBool {
    write("~/Error2")(error: $0) // The code to try
}.catch {
    println("write failure 2: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Также легко добавить средство повтора:

TryBool {
    write("~/Error3")(error: $0) // The code to try
}.retry {
    println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
    return write("~/Error3r")  // The code to retry
}.catch {
    println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Список для TryBool:

class TryBool {
    typealias Tryee = NSErrorPointer -> Bool
    typealias Catchee = NSError? -> ()
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return self.retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) {
        var error: NSError?
        for numRetries in 0...retries { // First try is retry 0
            error = nil
            let result = tryee(&error)
            if result {
                return
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        catchee(error)
    }
}

Вы можете написать аналогичный класс для тестирования дополнительного возвращаемого значения вместо значения Bool:

class TryOptional<T> {
    typealias Tryee = NSErrorPointer -> T?
    typealias Catchee = NSError? -> T
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) -> T {
        var error: NSError?
        for numRetries in 0...retries {
            error = nil
            let result = tryee(&error)
            if let r = result {
                return r
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        return catchee(error)
    }
}

В версии TryOptional применяется необязательный тип возвращаемого значения, что облегчает последующее программирование, например, «Swift Way:

struct FailableInitializer {
    init?(_ id: Int, error: NSErrorPointer) {
        // Always fails in example
        if error != nil {
            error.memory = NSError(domain: "", code: id, userInfo: [:])
        }
        return nil
    }
    private init() {
        // Empty in example
    }
    static let fallback = FailableInitializer()
}

func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
    return FailableInitializer(id, error: error)
}

var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
    println("failableInitializer failure code: \(failError!.code)")
    failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap

Использование TryOptional:

let failure2 = TryOptional {
    failableInitializer(2)(error: $0)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

let failure3 = TryOptional {
    failableInitializer(3)(error: $0)
}.retry {
    println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
    return failableInitializer(31)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

Обратите внимание на автоматическое развертывание.

Говард Ловатт
источник
7

Изменить: хотя этот ответ работает, он немного больше, чем Objective-C транслитерируется в Swift. Это было сделано устаревшими изменениями в Swift 2.0. Ответ Гильерма Торреса Кастро, приведенный выше, является очень хорошим введением в предпочтительный способ обработки ошибок в Swift. СДН

Потребовалось немного разобраться, но я думаю, что подозревал это. Это кажется некрасивым. Ничего, кроме тонкой кожи над версией Objective-C.

Вызов функции с параметром NSError ...

var fooError : NSError ? = nil

let someObject = foo(aParam, error:&fooError)

// Check something was returned and look for an error if it wasn't.
if !someObject {
   if let error = fooError {
      // Handle error
      NSLog("This happened: \(error.localizedDescription)")
   }
} else {
   // Handle success
}`

Написание функции, которая принимает параметр ошибки ...

func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {

   // Do stuff...

   if somethingBadHasHappened {
      if error {
         error.memory = NSError(domain: domain, code: code, userInfo: [:])
      }
      return nil
   }

   // Do more stuff...
}
Винс О'Салливан
источник
Источник: developer.apple.com/library/prerelease/ios/documentation/swift/…
Маркус Раутопуро
5

Базовая оболочка вокруг цели C, которая дает вам функцию try catch. https://github.com/williamFalcon/SwiftTryCatch

Используйте как:

SwiftTryCatch.try({ () -> Void in
        //try something
     }, catch: { (error) -> Void in
        //handle error
     }, finally: { () -> Void in
        //close resources
})
Уильям Сокол
источник
Отличная идея. Но кто решает использовать это, должен иметь в виду, что объекты, выделенные в блоке try, не освобождаются при возникновении исключения. Это может вызвать проблемы объектов-зомби, и любое использование RAII скомпрометировано (авто-разблокировка, авто-sql-commit, авто-sql-rollback ...). Может быть, C ++ может помочь нам с какой-то формой «runAtExit»?
hariseldon78
Обновление: я только что обнаружил, что в clang есть флаг, позволяющий освобождать объекты при выдаче исключений: -fobjc-arc-exceptions. Я должен попробовать, если это все еще работает с упакованной версией (я думаю, что это должно)
hariseldon78
Если вы используете эту опцию, имейте в виду, что размер кода увеличивается, так как компилятор должен генерировать полуобезопасный код. Также: полагаться на такую ​​функцию компилятора, возможно, не лучшая идея. Исключения составляют только ошибки программиста, поэтому использование этого параметра компилятора для экономии немного памяти во время разработки не стоит. Если у вас есть исключения в вашем рабочем коде, вы должны в первую очередь иметь дело с тем, что вызывает эти исключения.
Кристиан Кинле
1
Там могут быть ситуации вне вашего контроля. Например, разбор json в неправильном формате.
Уильям Сокол
3

Это обновленный ответ для Swift 2.0. Я с нетерпением жду многофункциональную модель обработки ошибок, как в Java. Наконец, они объявили хорошие новости. Вот

Модель обработки ошибок: новая модель обработки ошибок в Swift 2.0 сразу почувствует себя естественной, со знакомыми ключевыми словами try, throw и catch . Лучше всего то, что он был разработан для идеальной работы с Apple SDK и NSError. На самом деле, NSError соответствует Swift ErrorType. Вы определенно захотите посмотреть сеанс WWDC «Что нового в Swift», чтобы узнать об этом больше.

например:

func loadData() throws { }
func test() {
do {
    try loadData()
} catch {
    print(error)
}}
Paraneetharan Saravanaperumal
источник
3

Как сказал Гильерме Торрес Кастро, в Swift 2.0, try, catch, doмогут быть использованы при программировании.

Например, в CoreData выборки метод данных, вместо того , чтобы положить в &errorкачестве параметра в managedContext.executeFetchRequest(fetchRequest, error: &error), теперь нужно использовать только использовать , managedContext.executeFetchRequest(fetchRequest)а затем обработать ошибку с try, catch( Apple , Document Link )

do {
   let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
   if let results = fetchedResults{
      people = results
   }
} catch {
   print("Could not fetch")
}

Если вы уже загрузили бета-версию xcode7. Попробуйте выполнить поиск ошибок в Документациях и Справочнике по API и выберите первый показывающий результат, он дает общее представление о том, что можно сделать для этого нового синтаксиса. Однако полная документация еще не опубликована для многих API.

Более причудливые методы обработки ошибок можно найти в

Что нового в Swift (сессия 2015 года 106 28м30 с)

Zingoer
источник
1

Хорошая и простая библиотека для обработки исключений: TryCatchFinally-Swift

Как и некоторые другие, он оборачивается объективными функциями C-исключений.

Используйте это так:

try {
    println("  try")
}.catch { e in
    println("  catch")
}.finally {
    println("  finally")
}
Мортен Холмгаард
источник
Я добавил образец :)
Мортен Холмгаард
Вероятно, стоит упомянуть мнение авторов: «Предупреждение: это хак для веселья и зла. Не поддавайтесь искушению использовать его».
jbat100
1

Начиная с Swift 2, как уже упоминалось, обработка ошибок лучше всего достигается с помощью перечислений do / try / catch и ErrorType. Это работает довольно хорошо для синхронных методов, но для асинхронной обработки ошибок требуется немного хитрости.

Эта статья имеет отличный подход к этой проблеме:

https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/

Подвести итоги:

// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData

// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
    {
    completionHandler()
    }

тогда вызов вышеуказанного метода будет следующим:

self.loadData("someString",
    completionHandler:     
        { result: LoadDataResult in
        do
            {
            let data = try result()
            // success - go ahead and work with the data
            }
        catch
            {
            // failure - look at the error code and handle accordingly
            }
        })

Это выглядит немного чище, чем передача отдельного обратного вызова errorHandler в асинхронную функцию, как это было сделано до Swift 2.

Джин Лопарко
источник
0

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

cheborneck
источник
1
Возврат nil не возвращает никакой информации о природе ошибки. Если объект ошибки возвращается при возникновении ошибки, то, в зависимости от ошибки, программист может решить игнорировать ее, обрабатывать, разрешать всплывать или «выдавать сообщение или что-то еще». Знание - сила.
Винс О'Салливан