Swift 2: Call может бросить, но он не помечен как «try» и ошибка не обрабатывается

161

После того, как я установил бета-версию Xcode 7 и преобразовал свой swift-код в 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для некоторой специальной очистки. См. Exceptionsafecode.com для получения дополнительной информации (
речь идет
Но как бы вы запустили функцию? Если я пойду с @ 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редактируемого кода.

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

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

  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)» .

bames53
источник
В принципе поймать себя, может обработать ошибку? или функция ввода
Фархад
1
@BrianS Я не совсем уверен, что именно вы спрашиваете, особенно в отношении «функции ввода», но «catch» по сути является синонимом «handle» в контексте исключений. То есть перехват исключения и обработка исключения - это одно и то же, что касается языков программирования.
bames53
У меня есть одна ошибка, я тихо не понимаю, Не могли бы вы помочь мне? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Фархад
@ BrianS Похоже, вы используете функцию с неправильной подписью где-то. Что-то ожидает, что будет предоставлена ​​функция, которая принимает в NSData?, NSURLResponse?, NSError?качестве аргументов, но вы даете ему функцию, которая не принимает никаких аргументов.
bames53
Или что-то ожидает, что функция, которая не была объявлена, генерирует исключения, а вы предоставляете ей функцию, которая генерирует исключения.
bames53