Swift 2: Виклик може кинутись, але він не позначений "спробувати" і помилка не обробляється


161

Після того як я встановив бета-версію Xcode 7 і перетворив свій швидкий код на Swift 2, у мене з’явилася проблема з кодом, яку я не можу з’ясувати. Я знаю, що Swift 2 є новим, тому я шукаю і з’ясовую, оскільки про це нічого немає, я повинен написати запитання.

Ось помилка:

Виклик може кинутись, але він не позначений "спробувати" і помилка не обробляється

Код:

func deleteAccountDetail(){
        let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
        let request = NSFetchRequest()
        request.entity = entityDescription

        //The Line Below is where i expect the error
        let fetchedEntities = self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
        }

        do {
            try self.Context!.save()
        } catch _ {
        }

    }

Знімок: введіть тут опис зображення

Відповіді:


168

Ви повинні виявити помилку так само, як ви вже робите для свого save()дзвінка, і оскільки ви обробляєте тут декілька помилок, ви можете робити tryкілька дзвінків послідовно в одному блоці "do-catch", наприклад:

func deleteAccountDetail() {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()
    request.entity = entityDescription

    do {
        let fetchedEntities = try self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
            self.Context!.deleteObject(entity)
        }

        try self.Context!.save()
    } catch {
        print(error)
    }
}

Або, як @ bames53 вказував у коментарях нижче, часто краще практикувати не вловлювати помилку там, де її кинули. Ви можете позначити метод як throwsтоді tryдля виклику методу. Наприклад:

func deleteAccountDetail() throws {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()

    request.entity = entityDescription

    let fetchedEntities = try Context.executeFetchRequest(request) as! [AccountDetail]

    for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
    }

    try self.Context!.save()
}

Це допоможе мені зрозуміти це, дякую.
Фархад

5
Насправді тут не потрібно, щоб виняток було спіймано тут. Можна просто додати tryключове слово до виклику функції та оголосити цю функцію як func deleteAccountDetail() throw. Або якщо ви гарантували, що функція не буде кинутий для даного вводу, ви можете використовувати try!.
bames53

4
Я доводжу це не для того, щоб виправити помилки, а тому, що насправді дуже важливо пристойне поводження з помилками, яке базується на винятках, що більшість місць, де трапляються винятки, не складають винятків. Є три види місць, де вилов винятків є доцільним. В усіх інших місцях код не повинен чітко обробляти винятки, а також повинен покладатися на неявні deinit()виклики очищення (тобто RAII) або періодично використовувати deferдля чищення спеціальних операцій. Докладніше див. У виняткахafecode.com (У ньому йдеться про C ++, але основні принципи застосовуються і до винятків Swift.)
bames53

Але як би ви запустили функцію? Якщо я піду @ @ bames53?
Фархад

1
@ NickMoore Те, що розробники Swift вирішили зателефонувати їм, не має значення в тому, якими вони є насправді. Нова система поводження з помилками Swift - це впровадження винятків, оскільки цей термін зазвичай використовується в решті галузі.
bames53

41

Під час виклику функції, яка задекларована throwsу Swift, потрібно анотувати анотацію сайту виклику за допомогою tryабо try!. Наприклад, задана функція метання:

func willOnlyThrowIfTrue(value: Bool) throws {
  if value { throw someError }
}

цю функцію можна назвати так:

func foo(value: Bool) throws {
  try willOnlyThrowIfTrue(value)
}

Тут ми анотуємо виклик try, який закликає читача, що ця функція може кинути виняток, і будь-які наступні рядки коду можуть не виконуватись. Нам також потрібно анотувати цю функцію throws, оскільки ця функція може кинути виняток (тобто, коли willOnlyThrowIfTrue()кидає, fooвиключає автоматично викид вгору.

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

func bar() {
  try! willOnlyThrowIfTrue(false)
}

Таким чином, коли ви гарантуєте, що код не викине, вам не доведеться вводити додатковий код котла, щоб відключити поширення виключень.

try!виконується під час виконання: якщо ви використовуєте, try!а функція закінчується кидком, виконання програми буде припинено з помилкою виконання.

Більшість кодів обробки винятків має виглядати вище: ви просто поширюєте винятки вгору, коли вони виникають, або ви встановлюєте умови, щоб інакше можливі винятки не були виключені. Будь-яке очищення інших ресурсів у вашому коді повинно відбуватися шляхом знищення об'єктів (тобто deinit()) або іноді за допомогою deferкоду ed.

func baz(value: Bool) throws {

  var filePath = NSBundle.mainBundle().pathForResource("theFile", ofType:"txt")
  var data = NSData(contentsOfFile:filePath)

  try willOnlyThrowIfTrue(value)

  // data and filePath automatically cleaned up, even when an exception occurs.
}

Якщо з будь-якої причини у вас є код очищення, який потрібно запустити, але він не deinit()функціонує, ви можете використовувати defer.

func qux(value: Bool) throws {
  defer {
    print("this code runs when the function exits, even when it exits by an exception")
  }

  try willOnlyThrowIfTrue(value)
}

Більшість кодів, які мають справу з винятками, просто поширюють їх на абонентів, роблячи очищення в дорозі через deinit()або defer. Це тому, що більшість кодів не знає, що робити з помилками; він знає, що пішло не так, але у нього недостатньо інформації про те, що намагається зробити якийсь код вищого рівня, щоб знати, що робити з помилкою. Він не знає, чи підходить представлення діалогу користувачеві, чи слід його повторити, чи чи підходить щось інше.

Код вищого рівня, однак, повинен точно знати, що робити у випадку будь-якої помилки. Тож винятки дозволяють певним помилкам підніматися з місця, де вони спочатку трапляються, до місця, де їх можна обробити.

Обробка винятків здійснюється за допомогою catchоператорів.

func quux(value: Bool) {
  do {
    try willOnlyThrowIfTrue(value)
  } catch {
    // handle error
  }
}

Ви можете мати декілька заяв про вилов, кожен із яких має різний виняток.

  do {
    try someFunctionThatThowsDifferentExceptions()
  } catch MyErrorType.errorA {
    // handle errorA
  } catch MyErrorType.errorB {
    // handle errorB
  } catch {
    // handle other errors
  }

Більш детальну інформацію про кращі практики з винятками див . На веб-сторінці http://exceptionsafecode.com/ . Він спеціально спрямований на C ++, але, вивчивши модель виключень Swift, я вважаю, що основи стосуються і Swift.

Детальніше про модель синтаксису та помилки Swift див. У книзі Мова програмування Swift (Swift 2 Prerelease) .


В основному сам улов може впоратися з помилкою? або функція введення
Farhad

1
@BrianS Я не впевнений, що саме ви запитуєте, особливо стосовно "функції введення", але "catch", по суті, є синонімом "обробляти" в контексті винятків. Тобто, вилов винятку та обробка виключення - це одне й те саме, що стосується мов програмування.
bames53

У мене є одна помилка, яку я тихо не розумію, чи можете ви мені допомогти? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Фархад

@BrianS Це здається, що ви десь використовуєте функцію з неправильним підписом. Щось очікує, що йому буде надана функція, яка бере NSData?, NSURLResponse?, NSError?аргументи, але ви надаєте їй функцію, яка не приймає жодних аргументів.
bames53

Або щось очікує, що функція, яка не оголошена, кидає винятки, і ви надаєте їй функцію, яка видаляє винятки.
bames53
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.