Swift 2: la llamada se puede lanzar, pero no está marcada con 'try' y el error no se maneja

161

Después de instalar Xcode 7 beta y convertir mi código swift a Swift 2, tuve un problema con el código que no puedo entender. Sé que Swift 2 es nuevo, así que busco y descubro que no hay nada al respecto, debería escribir una pregunta.

Aquí está el error:

La llamada se puede lanzar, pero no está marcada con 'intentar' y el error no se maneja

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ánea: ingrese la descripción de la imagen aquí

Farhad
fuente

Respuestas:

168

Debe detectar el error tal como ya lo está haciendo para su save()llamada y dado que está manejando múltiples errores aquí, puede realizar tryvarias llamadas secuencialmente en un solo bloque de captura automática, de esta manera:

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

O como @ bames53 señaló en los comentarios a continuación, a menudo es una mejor práctica no detectar el error donde se arrojó. Puede marcar el método como throwsentonces trypara llamar al método. Por ejemplo:

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
fuente
Esto me ayuda a resolverlo, gracias.
Farhad
55
En realidad no se requiere que la excepción se encuentre aquí. Es posible agregar la trypalabra clave a la llamada de función y declarar esta función como func deleteAccountDetail() throw. O si ha garantizado que la función no arrojará la entrada dada, puede usarla try!.
bames53
44
No menciono esto, pero debido a que en realidad es bastante importante para el manejo de errores basado en excepciones decentes, la mayoría de los lugares donde ocurren excepciones no capturan excepciones. Hay tres tipos de lugares donde es apropiado capturar excepciones. En todos los demás lugares, el código no debe manejar las excepciones explícitamente, y debe basarse en deinit()llamadas implícitas para hacer la limpieza (es decir, RAII), o en ocasiones usar deferpara hacer una limpieza ad hoc. Consulte exceptionsafecode.com para obtener más información (habla de C ++, pero los principios básicos también se aplican a las excepciones de Swift).
bames53
Pero, ¿cómo ejecutarías la función? Si voy con @ bames53 camino?
Farhad
1
@NickMoore Lo que los desarrolladores de Swift eligen llamarles no hace una diferencia en lo que realmente son. El nuevo sistema de manejo de errores de Swift es una implementación de excepciones, ya que ese término se usa comúnmente en el resto de la industria.
bames53
41

Al llamar a una función que se declara con throwsen Swift, debe anotar el sitio de llamada de función con tryo try!. Por ejemplo, dada una función de lanzamiento:

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

Esta función se puede llamar así:

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

Aquí anotamos la llamada con try, lo que indica al lector que esta función puede generar una excepción y que las siguientes líneas de código podrían no ejecutarse. También tenemos que anotar esta función con throws, porque esta función podría lanzar una excepción (es decir, cuando se willOnlyThrowIfTrue()lanza, fooautomáticamente volverá a lanzar la excepción hacia arriba).

Si desea llamar a una función que se declara como posiblemente lanzada, pero que sabe que no se lanzará en su caso porque le está dando la entrada correcta, puede usarla try!.

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

De esta manera, cuando garantiza que el código no se lanzará, no tiene que poner código extra repetitivo para deshabilitar la propagación de excepciones.

try!se aplica en tiempo de ejecución: si usa try!y la función termina lanzando, la ejecución de su programa terminará con un error de tiempo de ejecución.

La mayoría de los códigos de manejo de excepciones deben ser similares a los anteriores: o simplemente propaga las excepciones hacia arriba cuando ocurren, o configura las condiciones de modo que, de lo contrario, se descarten posibles excepciones. Cualquier limpieza de otros recursos en su código debe ocurrir a través de la destrucción de objetos (es decir deinit()), o algunas veces a través del 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.
}

Si por alguna razón tiene un código de limpieza que necesita ejecutarse pero no está en una deinit()función, puede usarlo 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)
}

La mayoría del código que trata con excepciones simplemente hace que se propaguen hacia arriba a las personas que llaman, haciendo la limpieza en el camino a través de deinit()o defer. Esto se debe a que la mayoría del código no sabe qué hacer con los errores; sabe qué salió mal, pero no tiene suficiente información sobre lo que intenta hacer un código de nivel superior para saber qué hacer con el error. No sabe si presentar un diálogo al usuario es apropiado, o si debería volver a intentarlo, o si algo más es apropiado.

Sin embargo, el código de nivel superior debe saber exactamente qué hacer en caso de error. Por lo tanto, las excepciones permiten que surjan errores específicos desde donde ocurren inicialmente hasta donde se pueden manejar.

El manejo de excepciones se realiza mediante catchdeclaraciones.

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

Puede tener varias declaraciones catch, cada una de las cuales captura un tipo diferente de excepción.

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

Para obtener más detalles sobre las mejores prácticas con excepciones, consulte http://exceptionsafecode.com/ . Está específicamente dirigido a C ++, pero después de examinar el modelo de excepción Swift, creo que los conceptos básicos se aplican también a Swift.

Para obtener detalles sobre el modelo de sintaxis y manejo de errores de Swift, consulte el libro El lenguaje de programación Swift (Presentación preliminar de Swift 2) .

bames53
fuente
Básicamente atrapar a sí mismo puede manejar el error? o función de entrada
Farhad
1
@BrianS No estoy seguro exactamente de lo que está preguntando, especialmente con respecto a una 'función de entrada', pero 'captura' es esencialmente un sinónimo de 'manejar' en el contexto de excepciones. Es decir, detectar una excepción y manejar una excepción son lo mismo, en lo que respecta a los lenguajes de programación.
bames53
Tengo un error que no entiendo, ¿me pueden ayudar? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad
@BrianS Parece que estás usando una función con una firma incorrecta en alguna parte. Algo espera que se le asigne una función que toma NSData?, NSURLResponse?, NSError?como argumentos, pero le está dando una función que no toma ningún argumento.
bames53
O algo espera que una función no declarada arroje excepciones y le está dando una función que arroja excepciones.
bames53