Swift 2: A chamada pode ser lançada, mas não está marcada com 'try' e o erro não é tratado

161

Depois de instalar o Xcode 7 beta e converter meu código swift em Swift 2, tive um problema com o código que não consigo descobrir. Eu sei que o Swift 2 é novo, então pesquiso e descubro que, como não há nada, devo escrever uma pergunta.

Aqui está o erro:

A chamada pode ser lançada, mas não está marcada com 'try' e o erro não é tratado

Código:

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

    }

Instantâneo: insira a descrição da imagem aqui

Farhad
fonte

Respostas:

168

Você precisa capturar o erro da mesma forma que já está fazendo na sua save()chamada e, como está lidando com vários erros aqui, é possível trycapturar várias chamadas sequencialmente em um único bloco de captura, como:

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

Ou como @ bames53 apontou nos comentários abaixo, geralmente é uma prática melhor não capturar o erro onde foi lançado. Você pode marcar o método como throwsentão trypara chamá-lo. Por exemplo:

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()
}
Mick MacCallum
fonte
Isso me ajuda a descobrir, obrigado.
Farhad
5
Na verdade, não é necessário que a exceção seja capturada aqui. É possível apenas adicionar a trypalavra-chave à chamada de função e declarar essa função como func deleteAccountDetail() throw. Ou se você garantiu que a função não será lançada para a entrada especificada, você pode usar try!.
usar o seguinte comando
4
Trago isso à tona não para nitpick, mas porque é realmente muito importante o tratamento de erros com base em exceções decentes, que a maioria dos lugares onde as exceções acontecem não as capturam. Existem três tipos de lugares onde capturar exceções é apropriado. Em todos os outros lugares, o código não deve lidar com exceções explicitamente e deve depender de deinit()chamadas implícitas para limpar (ou seja, RAII), ou ocasionalmente usar deferpara fazer uma limpeza ad hoc. Veja exceptionsafecode.com para mais (Ele fala sobre C ++, mas os princípios básicos se aplicam às exceções Swift também.)
bames53
Mas como você executaria a função? Se eu for com @ bames53 way?
Farhad
1
@NickMoore O que os desenvolvedores do Swift optam por chamá-los não faz diferença no que realmente são. O novo sistema de tratamento de erros da Swift é uma implementação de exceções, pois esse termo é comumente usado em todo o restante do setor.
bames53
41

Ao chamar uma função declarada com throwsno Swift, você deve anotar o site de chamada da função com tryou try!. Por exemplo, dada uma função de arremesso:

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

Essa função pode ser chamada como:

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

Aqui, anotamos a chamada com try, que chama ao leitor que esta função pode gerar uma exceção, e as seguintes linhas de código podem não ser executadas. Também temos que anotar esta função com throws, porque essa função pode lançar uma exceção (ou seja, quando willOnlyThrowIfTrue()lança, então foo, automaticamente, a exceção será repetida para cima.

Se você deseja chamar uma função que é declarada como possivelmente lançada, mas que você sabe que não será lançada no seu caso porque está fornecendo a entrada correta, você pode usar try!.

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

Dessa forma, quando você garante que o código não será lançado, não precisará inserir um código adicional para desabilitar a propagação de exceção.

try!é imposta no tempo de execução: se você usar try!e a função acabar sendo lançada, a execução do seu programa será encerrada com um erro de tempo de execução.

A maioria dos códigos de manipulação de exceções deve se parecer com o acima: ou você simplesmente propaga as exceções para cima quando elas ocorrem, ou configura condições para que as exceções possíveis sejam descartadas. Qualquer limpeza de outros recursos no seu código deve ocorrer por destruição de objeto (ou seja deinit()) ou, às vezes, por defercódigo 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.
}

Se, por qualquer motivo, você tiver um código de limpeza que precisa ser executado, mas não está em uma deinit()função, você pode usá-lo 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)
}

A maioria dos códigos que lida com exceções simplesmente os propaga para os chamadores, fazendo a limpeza no caminho via deinit()ou defer. Isso ocorre porque a maioria dos códigos não sabe o que fazer com erros; ele sabe o que deu errado, mas não possui informações suficientes sobre o que algum código de nível superior está tentando fazer para saber o que fazer com o erro. Ele não sabe se a apresentação de uma caixa de diálogo ao usuário é apropriada, ou se deve tentar novamente, ou se algo mais é apropriado.

Código de nível superior, no entanto, deve saber exatamente o que fazer no caso de qualquer erro. Portanto, as exceções permitem que erros específicos surjam de onde ocorrem inicialmente para onde podem ser manipulados.

O tratamento de exceções é feito por meio de catchinstruções.

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

Você pode ter várias instruções de captura, cada uma capturando um tipo diferente de exceção.

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

Para mais detalhes sobre as melhores práticas, com exceções, consulte http://exceptionsafecode.com/ . Ele é voltado especificamente para C ++, mas depois de examinar o modelo de exceção Swift, acredito que o básico também se aplica a Swift.

Para detalhes sobre a sintaxe do Swift e o modelo de tratamento de erros, consulte o livro A linguagem de programação Swift (Swift 2 Pré-lançamento) .

bames53
fonte
Basicamente pegar-se pode lidar com o erro? ou função de entrada
Farhad
1
@BrianS Não sei exatamente o que você está perguntando, especialmente no que diz respeito a uma 'função de entrada', mas 'catch' é essencialmente um sinônimo de 'handle' no contexto de exceções. Ou seja, capturar uma exceção e manipular uma exceção são a mesma coisa, no que diz respeito às linguagens de programação.
bames53
Eu tenho um erro que eu não entendo, você pode me ajudar? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad
@BrianS Parece que você está usando uma função com uma assinatura incorreta em algum lugar. Algo espera receber uma função que aceite NSData?, NSURLResponse?, NSError?como argumentos, mas você está atribuindo uma função que não aceita argumentos.
bames53
Ou algo espera que uma função não declarada gere exceções e você esteja atribuindo uma função que gere exceções.
precisa saber é o seguinte