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

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

Вот ошибка:

Звонок может вызывать, но он не отмечен "try", и ошибка не обрабатываются

Код:

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

    }

Снимок: enter image description here

Ответ 1

Вы должны поймать ошибку так же, как вы уже делаете для своего вызова save(), и поскольку вы обрабатываете несколько ошибок здесь, вы можете try несколько вызовов последовательно в одном блоке блокировки наложения, например

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

Ответ 2

При вызове функции, объявленной с помощью throws в Swift, вы должны аннотировать сайт вызова функции с помощью try или try!. Например, с учетом функции throwing:

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

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

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

Подробнее о передовых методах с исключениями см. http://exceptionsafecode.com/. Он специально нацелен на С++, но после изучения модели исключения Swift я считаю, что основы применимы и к Swift.

Подробнее о модели синтаксиса и обработки ошибок Swift см. книгу Язык Swift для программирования (Swift 2 Prerelease).