Виправлення помилок у швидкій мові


190

Я не надто багато читав у Swift, але одне, що я помітив, - це не винятки. То як вони роблять помилки в Swift? Хто-небудь знайшов щось, пов’язане з поводженням з помилками?


1
Я знайшов повідомлення про помилки так само, як у Obj-C: o
Арбітур

13
@Arbitur старий добрий segfault спосіб?
peko

Створив NSTimer у Swift, і коли я неправильно написав цю функцію, він вийшов з ладу і дав мені помилку, сказавши, що не може знайти метод :)
Arbitur

3
Ви можете додати підтримку спробу лову для Swift, дотримуючись вказівок у цій статті: medium.com/@_willfalcon/adding-try-catch-to-swift-71ab27bcb5b8
William Falcon

@peko Як ви обробляєте segfault у Swift? Я не думаю, що це можливо зараз, що, на жаль, робить деякі помилки непоправними
Орлін Георгієв,

Відповіді:


148

Швидкий 2 та 3

У Swift 2 все трохи змінилося, оскільки з'явився новий механізм поводження з помилками, який трохи більше схожий на винятки, але відрізняється деталізацією.

1. Вказівка ​​на можливість помилки

Якщо функція / метод хоче вказати, що може призвести до помилки, вона повинна містити таке throwsключове слово

func summonDefaultDragon() throws -> Dragon

Примітка. Немає специфікації для типу помилки, яку може насправді випустити функція. У цій декларації просто зазначено, що функція може кидати екземпляр будь-якого типу, що реалізує ErrorType або взагалі не кидає.

2. Функція виклику, яка може кидати помилки

Для виклику функції потрібно скористатися ключовим словом спробувати, як це

try summonDefaultDragon()

цей рядок зазвичай повинен бути присутнім блоком приховання, як це

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
}

Примітка. У пункті лову використовуйте всі потужні функції відповідності шаблону Swift, тому ви тут дуже гнучкі.

Ви можете вирішити поширити помилку, якщо ви викликаєте функцію кидання з функції, яка сама позначена throwsключовим словом:

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

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

let dragonOrNil = try? summonDefaultDragon()

Таким чином ви отримуєте повернене значення або нуль, якщо сталася якась помилка. Використовуючи цей спосіб, ви не отримуєте об'єкт помилки.

Що означає, що ви також можете поєднувати try?з корисними твердженнями, такими як:

if let dragon = try? summonDefaultDragon()

або

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

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

let dragon = try! summonDefaultDragon()

Якщо функція насправді видаляє помилку, ви отримаєте помилку виконання у вашій програмі, і програма припиниться.

3. Викидання помилки

Для того, щоб викинути помилку, ви використовуєте таке ключове слово

throw DragonError.dragonIsMissing

Ви можете кинути все, що відповідає ErrorTypeпротоколу. Для початківців NSErrorвідповідає цей протокол, але ви, ймовірно, хотіли б перейти на enum-основі, 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блок не буде вловлювати жодну NSException, і навпаки, для цього ви повинні використовувати ObjC.
  • Винятки Swift сумісні з умовами NSErrorметоду какао щодо повернення false(для Boolповернення функцій) або nil(для AnyObjectповернення функцій) та передачі NSErrorPointerз деталями помилок.

Як додатковий синтатичний цукор для полегшення поводження з помилками, є ще два поняття

  • відкладені дії (використовуючи defer ключового слова), які дозволяють досягти такого ж ефекту, що і, нарешті, блоки в Java / C # / тощо
  • заява-захист (за допомогою guardключового слова), яка дозволяє писати трохи менше коду if / else, ніж у звичайній коді перевірки помилок / сигналізації.

Швидкий 1

Помилки під час виконання:

Як Леандрос пропонує для обробки помилок виконання (наприклад, проблеми з підключенням до мережі, розбору даних, відкриття файлу тощо), вам слід користуватися так, 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ключове слово тощо). Однак, як свідчить документація, вона працює в той же час виконання, що і ObjC, і тому ви все одно можете кинути NSExceptionsтак:

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

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

Питання полягає в тому, чи варто викидати винятки за помилки програміста, чи скоріше використовувати твердження, як пропонує Apple у мовному посібнику.


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 Prerelease)". iBooks.

Приклад: (з книги)

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, The Swift Programming Language, схоже, з помилками слід керуватися за допомогою 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. "Мова швидкого програмування". iBooks. https://itun.es/br/jEUH0.l

Оновлення

З новинних книг Apple "Використання Swift з какао та Objective-C". Винятки з виконання не відбуваються за допомогою швидких мов, тому для вас немає спроб лову. Замість цього ви використовуєте необов'язкове ланцюжок .

Ось розтягнення з книги:

Наприклад, у списку кодів нижче перший та другий рядки не виконуються, оскільки властивість length та метод characterAtIndex: не існують на об’єкті NSDate. Константа myLength вважається необов'язковою Int і встановлюється на нуль. Ви також можете використовувати оператор 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". iBooks. 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". iBooks. https://itun.es/br/1u3-0.l


Для останнього твердження воно повинно бути: виконайте {спробуйте myString.writeToFile (шлях, атомно: істина: кодування: NSUTF8StringEncoding)} уловлюйте помилку як NSError {print (error)}
Jacky

1
@Jacky Так, це справедливо для swift 2.0, хоча ця відповідь була написана до випуску swift 2.0, я оновив відповідь, щоб показати нові способи обробки помилок у swift 2.0. Я думав, щоб дозволити цей шлях для довідок, але я буду розглядати оновлення всієї відповіді, щоб використовувати лише швидкий 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)")
}

6
Це викликає два питання: що відбувається, коли код ObjC, який ми викликаємо від Swift, насправді викидає виняток, і чи NSError є нашим універсальним об'єктом помилки, як у ObjC?
MDJ

1
Це просто факт життя зі Свіфт, який ініціалізаторами не може чи не може вийти з ладу?
Філ

11
Обробка винятків виглядає досить брудно
Tash Pemhiwa

27
Так, кому потрібні винятки, коли ти можеш просто розбитися? Або поставити NSError ** як аргумент у всі функції, які ви оголошуєте? так що кожен f();g();стає f(&err);if(err) return;g(&err);if(err) return;на перший місяць, тоді він просто стаєf(nil);g(nil);hopeToGetHereAlive();
hariseldon78

2
Ця відповідь застаріла (Swift тепер підтримує винятки) і неправильна (Objective-C підтримує винятки.
Rog

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)
    }
}

Версія TryOtional застосовує необов'язковий тип повернення, що спрощує подальше програмування, наприклад, "Швидкий шлях:

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

Використання TryOtional:

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

Редагувати: Хоча ця відповідь спрацьовує, це трохи більше, ніж об’єктив-C, транслітерований у Swift. Він був застарілий змінами в Swift 2.0. Відповідь Гільгерма Торреса Кастро - це дуже вдале введення у бажаний спосіб поводження з помилками у Swift. VOS

Це знадобилося трохи з'ясувати це, але я думаю, що я його придушив. Це здається некрасивим. Не більше ніж тонка шкіра над версією 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...
}


5

Основна обгортка навколо об'єкта C, що дає вам можливість спробувати улов. https://github.com/williamFalcon/SwiftTryCatch

Використовуйте як:

SwiftTryCatch.try({ () -> Void in
        //try something
     }, catch: { (error) -> Void in
        //handle error
     }, finally: { () -> Void in
        //close resources
})

Гарна ідея. Але хто вирішить скористатись цим, майте на увазі, що об'єкти, виділені у блоці спробу, не розміщуються, коли викидається виняток. Це може спричинити проблеми із зомбі-об’єктами, і будь-яке використання RAII поставлено під загрозу (автоматичне розблокування, auto-sql-фіксація, авто-sql-відкат ...). Можливо, c ++ може допомогти нам з якоюсь формою "runAtExit"?
hariseldon78

Оновлення: я щойно встановив, що в кланге є прапор, що дозволяє випускати об'єкти при викиді винятків: -fobjc-arc-виключення. Я повинен спробувати, якщо він все ще працює з завершеною версією (я думаю, що це повинно бути)
hariseldon78

Якщо ви використовуєте цю опцію, пам’ятайте, що розмір коду збільшується, оскільки компілятор повинен генерувати безпечний для напіввиключення код. Також: Покладання на таку функцію компілятора може бути не найкращою ідеєю. Винятки складаються лише з помилок програміста, так що виправляти цей варіант компілятора просто для економії небагато пам'яті під час розробки не варто. Якщо у вашому виробничому коді є винятки, вам слід в першу чергу розібратися з тим, що спричинило ці винятки.
Крістіан Кіенле

1
Можуть виникнути ситуації з вашого контролю. Наприклад, розбір json у неправильному форматі.
Вільям Сокіл

3

Це відповідь на оновлення для швидкого 2.0. Я з нетерпінням чекаю багатофункціональної моделі обробки помилок, як у Java. Нарешті вони оголосили добру новину. тут

Модель обробки помилок: Нова модель поводження з помилками у Swift 2.0 миттєво відчує себе природно, із знайомими ключовими словами пробувати, кидати та ловити . Найкраще, що він був розроблений для того, щоб ідеально співпрацювати з Apple SDK та NSError. Насправді, NSError відповідає типу помилки Swift. Ви обов'язково захочете переглянути сесію WWDC на тему «Що нового в Swift», щоб дізнатися більше про неї.

наприклад:

func loadData() throws { }
func test() {
do {
    try loadData()
} catch {
    print(error)
}}

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. Спробуйте пошукати помилки в кидці в Documentations і API Reference та виберіть перший показ результату, він дає основне уявлення, що можна зробити для цього нового синтаксису. Однак повністю документація ще не розміщена для багатьох API.

Більше фантазійних методів обробки помилок можна знайти в

Що нового у Swift (сесія 2015 року, 28 2830)



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
            }
        })

Це здається дещо чистішим, ніж окремий зворотний виклик помилки Handler переданий асинхронній функції, як це було б оброблено до Swift 2.


0

Що я бачив, це те, що з-за характеру пристрою ви не хочете, щоб викидали користувачеві повідомлення з критичними помилками. Ось чому більшість функцій повертає необов’язкові значення, тоді ви просто кодуєте, щоб ігнорувати необов'язкові. Якщо функція повертається до нуля, це означає, що вона не вдалася, ви можете надіслати повідомлення чи що завгодно.


1
Повернення нуля не повертає ніякої інформації про характер помилки. Якщо об’єкт помилки повертається, коли виникає помилка, тоді, залежно від помилки, програміст може вибрати, щоб проігнорувати його, обробити його, дозволити йому міхур або "вискакувати повідомлення чи що завгодно". Знання це сила.
Вінс О'Салліван
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.