¡Intenta intenta! & ¿tratar? ¿Cuál es la diferencia y cuándo usar cada uno?

172

En Swift 2.0 , Apple introdujo una nueva forma de manejar los errores (do-try-catch). Y hace unos días en Beta 6 se introdujo una palabra clave aún más nueva ( try?). Además, sabía que puedo usar try!. ¿Cuál es la diferencia entre las 3 palabras clave y cuándo usar cada una?

Abdurrahman
fuente

Respuestas:

316

Actualizado para Swift 5.1

Asuma la siguiente función de lanzamiento:

enum ThrowableError: Error {

    case badError(howBad: Int)
}

func doSomething(everythingIsFine: Bool = false) throws -> String {

  if everythingIsFine {
      return "Everything is ok"
  } else {
      throw ThrowableError.badError(howBad: 4)
  }
}

tratar

Tiene 2 opciones cuando intenta llamar a una función que puede lanzar.

Puede asumir la responsabilidad de manejar los errores rodeando su llamada dentro de un bloque de captura automática:

do {
    let result = try doSomething()
}
catch ThrowableError.badError(let howBad) {
    // Here you know about the error
    // Feel free to handle or to re-throw

    // 1. Handle
    print("Bad Error (How Bad Level: \(howBad)")

    // 2. Re-throw
    throw ThrowableError.badError(howBad: howBad)
}

O simplemente intente llamar a la función y pasar el error a la siguiente persona que llama en la cadena de llamadas:

func doSomeOtherThing() throws -> Void {    
    // Not within a do-catch block.
    // Any errors will be re-thrown to callers.
    let result = try doSomething()
}

¡tratar!

¿Qué sucede cuando intenta acceder a una opción implícitamente desenvuelta con un cero dentro de ella? Sí, es cierto, la aplicación se CRASH! Lo mismo ocurre con el intento! Básicamente ignora la cadena de error y declara una situación de "hacer o morir". Si la función llamada no arrojó ningún error, todo va bien. Pero si falla y arroja un error, su aplicación simplemente se bloqueará .

let result = try! doSomething() // if an error was thrown, CRASH!

¿tratar?

Una nueva palabra clave que se introdujo en Xcode 7 beta 6. Devuelve un opcional que desenvuelve valores exitosos y detecta errores al devolver nil.

if let result = try? doSomething() {
    // doSomething succeeded, and result is unwrapped.
} else {
    // Ouch, doSomething() threw an error.
}

O podemos usar guardia:

guard let result = try? doSomething() else {
    // Ouch, doSomething() threw an error.
}
// doSomething succeeded, and result is unwrapped.

Una nota final aquí, al usar la try?nota de que está descartando el error que ocurrió, ya que se traduce en cero. Usar intento? cuando te enfocas más en los éxitos y los fracasos, no en por qué fallaron las cosas.

¿Usando el operador de fusión?

¿Puedes usar el operador de fusión? con intento? para proporcionar un valor predeterminado en caso de falla:

let result = (try? doSomething()) ?? "Default Value"
print(result) // Default Value
Abdurrahman
fuente
Su segundo ejemplo de código ( let result = try doSomething() // Not within a do-catch block) se llamará desde un método que se declare como throws, ¿verdad? Entonces, si doSomething()falla, ¿también lo hace el método externo (a su vez)?
Nicolas Miari
Hilo antiguo y todo pero encontré eso hoy (Swift 4, Xcode 9.1) intentar? no desenvuelve automáticamente el resultado. Simplemente lo deja como una opción normal para que pueda desenvolver manualmente. No estoy seguro si esto cambió desde Swift 2/3, pero es por los documentos: developer.apple.com/library/content/documentation/Swift/… (consulte Conversión de errores a valores opcionales ). Gran explicación de intentar por cierto.
the_dude_abides
1
en swift 4, intente? no elimina las funciones de no llamar a lanzamiento que se producen dentro de las expresiones 'try' en mi proyecto.
aznelite89
77
También puede usar try?con, ??por lo que le permitiría definir un valor predeterminado en una línea:let something:String = (try? whateverIfItThrows()) ?? "Your default value here"
itMaxence