193 votes

Traitement des erreurs en langue swift

Je n'ai pas lu trop de choses sur Swift, mais j'ai remarqué qu'il n'y a pas d'exception. Alors comment fait-on pour gérer les erreurs en Swift ? Est-ce que quelqu'un a trouvé quelque chose en rapport avec la gestion des erreurs ?

1 votes

J'ai trouvé des messages d'erreur comme avec Obj-C :o

15 votes

@Arbitur la bonne vieille méthode segfault ?

0 votes

J'ai créé un NSTimer en Swift et lorsque j'ai mal orthographié la fonction, il s'est écrasé et m'a donné une erreur disant qu'il ne pouvait pas trouver la méthode :)

150voto

MDJ Points 793

Les erreurs d'exécution:

Comme le leandros suggère pour le traitement des erreurs d'exécution (comme des problèmes de connectivité réseau, l'analyse des données, de l'ouverture du fichier, etc), vous devez utiliser NSError comme vous l'avez fait en ObjC, parce que la Fondation, AppKit, UIKit, etc rapport à leurs erreurs de cette façon. Il est donc plus cadre de chose que de la langue de chose.

Un autre fréquentes modèle qui est utilisé sont séparateur de succès ou d'échec de blocs comme dans AFNetworking:

var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
    success: { (NSURLSessionDataTask) -> Void in
        println("Success")
    },
    failure:{ (NSURLSessionDataTask, NSError) -> Void in
        println("Failure")
    })

Encore l'échec bloc fréquemment reçu NSError instance, décrivant l'erreur.

Programmeur erreurs:

Pour le programmeur erreurs (comme en dehors des limites de l'accès de l'élément de tableau, non valide les arguments passés à un appel de fonction, etc) vous avez utilisé des exceptions dans ObjC. Swift de la langue ne semble pas avoir de prise en charge linguistique pour les exceptions (comme throw, catch, mot-clé, etc). Cependant, comme le suggère la documentation, il est en cours d'exécution sur le même runtime comme ObjC, et, par conséquent, vous êtes toujours en mesure de jeter NSExceptions comme ceci:

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

Vous juste ne peut pas les attraper dans le plus pur Swift, bien que vous pouvez opter pour attraper les exceptions en ObjC code.

La question est de savoir si vous devez lancer des exceptions pour les erreurs du développeur, ou plutôt l'utilisation d'affirmations comme Apple indique dans le guide de langue.

20 votes

"Les problèmes de connectivité réseau et l'ouverture de fichiers à l'aide des API Cocoa (NSFileHandle) peuvent générer des exceptions qui doivent être capturées. Sans exceptions dans Swift, vous devez implémenter cette partie de votre programme en Objective-C ou effectuer tout votre travail en utilisant les API BSD C (qui sont toutes deux des solutions de contournement médiocres). Voir la documentation de NSFileHandle.writeData pour plus d'informations... developer.apple.com/library/ios/documentation/Cocoa/Reference/ :

5 votes

Là encore, l'absence de gestion des exceptions signifie une construction d'objets en deux étapes, avec tous les problèmes inhérents. Voir aussi stroustrup.com/except.pdf .

2 votes

Les fatalError(...) est également identique.

70voto

Mise à jour du 9 juin 2015 - Très important

Swift 2.0 est livré avec try , throw y catch et le plus excitant est le suivant :

Swift traduit automatiquement les méthodes Objective-C qui produisent des erreurs en méthodes qui lancent une erreur conformément à la fonctionnalité native de traitement des erreurs de Swift.

Note : Les méthodes qui consomment des erreurs, telles que les méthodes déléguées ou les méthodes qui prennent un gestionnaire d'achèvement avec un argument d'objet NSError, ne sont pas deviennent pas des méthodes qui lancent lorsqu'elles sont importées par Swift.

Extrait de : Apple Inc. "Using Swift with Cocoa and Objective-C (Swift 2 Prerelease)" iBooks.

Exemple : (extrait du livre)

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
    NSLog(@"Error: %@", error.domain);
}

L'équivalent en swift sera :

let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
    try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
    print ("Error: \(error.domain)")
}

Lancer une erreur :

*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]

sera automatiquement transmise à l'appelant :

throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)

D'après les livres d'Apple, The Swift Programming Language, il semble que les erreurs devraient être gérées à l'aide d'enum.

Voici un exemple tiré du livre.

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure...  \(error)"
}

De : Apple Inc. "Le langage de programmation Swift". iBooks. <a href="https://itun.es/br/jEUH0.l" rel="nofollow">https://itun.es/br/jEUH0.l</a>

Mise à jour

Tiré des livres de nouvelles d'Apple, "Using Swift with Cocoa and Objective-C". Les exceptions d'exécution ne se produisent pas avec les langages Swift, c'est pourquoi vous n'avez pas try-catch. Au lieu de cela, vous utilisez Chaînage optionnel .

Voici un extrait du livre :

Par exemple, dans la liste de codes ci-dessous, les première et deuxième lignes ne sont pas exécutées parce qu'il n'y a pas d'autre solution. ne sont pas exécutées car la propriété length et la méthode characterAtIndex : n'existent pas sur un objet NSDate. La constante myLength est est déduite comme étant un Int optionnel et est définie à nil. Vous pouvez également utiliser un énoncé if-let pour déballer conditionnellement le résultat d'une méthode que l'objet l'objet peut ne pas répondre, comme le montre la ligne trois

let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
    println("Found \(fifthCharacter) at index 5")
}

Extrait de : Apple Inc. "Using Swift with Cocoa and Objective-C." iBooks. <a href="https://itun.es/br/1u3-0.l" rel="nofollow">https://itun.es/br/1u3-0.l</a>


Les livres vous encouragent également à utiliser le modèle d'erreur cacao d'Objective-C (NSError Object).

Les rapports d'erreur en Swift suivent le même modèle qu'en Objective-C, avec l'avantage supplémentaire d'offrir des valeurs de retour optionnelles. optionnelles. Dans le cas le plus simple, vous retournez une valeur Bool de la fonction pour indiquer si elle a réussi ou non. Lorsque vous devez signaler la raison de l'erreur, vous pouvez ajouter à la fonction un paramètre de sortie NSError de type NSError. NSError de type NSErrorPointer. Ce type est à peu près équivalent au NSError ** de l'Objective-C, avec une sécurité mémoire supplémentaire et un typage optionnel. Vous pouvez utiliser le préfixe & pour passer une référence à une NSError optionnelle. une référence à un type NSError optionnel en tant qu'objet NSErrorPointer, comme le montre l'exemple suivant comme le montre le code ci-dessous.

var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
    encoding: NSUTF8StringEncoding,
    error: &writeError)
if !written {
    if let error = writeError {
        println("write failure: \(error.localizedDescription)")
    }
}

Extrait de : Apple Inc. "Using Swift with Cocoa and Objective-C." iBooks. <a href="https://itun.es/br/1u3-0.l" rel="nofollow">https://itun.es/br/1u3-0.l</a>

0 votes

La dernière instruction devrait être : do { try myString.writeToFile(path, atomically : true, encoding : NSUTF8StringEncoding) }catch let error as NSError { print(error) }

1 votes

@Jacky Oui, c'est vrai pour swift 2.0 bien que cette réponse ait été écrite avant la sortie de swift 2.0, j'ai mis à jour la réponse pour montrer la nouvelle façon de gérer les erreurs dans swift 2.0. Je pensais laisser cette méthode comme référence, mais je vais envisager de mettre à jour l'ensemble de la réponse pour n'utiliser que swift 2.0.

13voto

Leandros Points 5916

Il n'y a pas d'exceptions en Swift, à l'instar de l'approche de l'Objective-C.

Dans le cadre du développement, vous pouvez utiliser assert pour détecter les erreurs qui pourraient apparaître et qui doivent être corrigées avant de passer à la production.

Le classique NSError n'est pas modifiée, vous envoyez un NSErrorPointer qui est alimenté.

Bref exemple :

var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
    println("An error occurred \(error)")
} else {
    println("Contents: \(contents)")
}

6 votes

Cela soulève deux questions : que se passe-t-il lorsque le code ObjC que nous appelons depuis Swift lève une exception, et si NSError est notre objet d'erreur universel comme dans ObjC ?

0 votes

Je dois tester la première plus tard, mais pour la seconde/ Oui, c'est le cas.

1 votes

Est-ce que c'est juste un fait de la vie avec Swift que les initialisateurs n'échouent pas ou ne peuvent pas échouer ?

12voto

Howard Lovatt Points 94

La "voie rapide" recommandée est la suivante :

func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
    return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}

var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
    println("write failure 1: \(writeError!.localizedDescription)")
    // assert(false) // Terminate program
}

Cependant, je préfère try/catch car je trouve qu'il est plus facile à suivre parce qu'il déplace la gestion des erreurs dans un bloc séparé à la fin, cet arrangement est parfois appelé "Golden Path" (chemin d'or). Heureusement, vous pouvez faire cela avec les fermetures :

TryBool {
    write("~/Error2")(error: $0) // The code to try
}.catch {
    println("write failure 2: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Il est également facile d'ajouter une fonction de réessai :

TryBool {
    write("~/Error3")(error: $0) // The code to try
}.retry {
    println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
    return write("~/Error3r")  // The code to retry
}.catch {
    println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

La liste de TryBool est la suivante :

class TryBool {
    typealias Tryee = NSErrorPointer -> Bool
    typealias Catchee = NSError? -> ()
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return self.retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) {
        var error: NSError?
        for numRetries in 0...retries { // First try is retry 0
            error = nil
            let result = tryee(&error)
            if result {
                return
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        catchee(error)
    }
}

Y

class TryOptional<T> {
    typealias Tryee = NSErrorPointer -> T?
    typealias Catchee = NSError? -> T
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) -> T {
        var error: NSError?
        for numRetries in 0...retries {
            error = nil
            let result = tryee(&error)
            if let r = result {
                return r
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        return catchee(error)
    }
}

La version TryOptional impose un type de retour non optionnel qui facilite la programmation ultérieure, par exemple "Swift Way" :

struct FailableInitializer {
    init?(_ id: Int, error: NSErrorPointer) {
        // Always fails in example
        if error != nil {
            error.memory = NSError(domain: "", code: id, userInfo: [:])
        }
        return nil
    }
    private init() {
        // Empty in example
    }
    static let fallback = FailableInitializer()
}

func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
    return FailableInitializer(id, error: error)
}

var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
    println("failableInitializer failure code: \(failError!.code)")
    failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap

Utilisation de TryOptional :

let failure2 = TryOptional {
    failableInitializer(2)(error: $0)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

let failure3 = TryOptional {
    failableInitializer(3)(error: $0)
}.retry {
    println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
    return failableInitializer(31)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

Noter le déballage automatique.

8voto

Vince O'Sullivan Points 380

Edita: Bien que cette réponse fonctionne, elle n'est guère plus que l'Objective-C translittéré en Swift. Elle a été rendue obsolète par les changements de Swift 2.0. La réponse de Guilherme Torres Castro ci-dessus est une très bonne introduction à la manière préférée de gérer les erreurs en Swift. VOS

Il m'a fallu un peu de temps pour trouver la solution, mais je crois que je l'ai trouvée. C'est quand même moche. Ce n'est rien d'autre qu'une peau fine sur la version Objective-C.

Appel d'une fonction avec un paramètre NSError...

var fooError : NSError ? = nil

let someObject = foo(aParam, error:&fooError)

// Check something was returned and look for an error if it wasn't.
if !someObject {
   if let error = fooError {
      // Handle error
      NSLog("This happened: \(error.localizedDescription)")
   }
} else {
   // Handle success
}`

Écrire la fonction qui prend un paramètre d'erreur...

func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {

   // Do stuff...

   if somethingBadHasHappened {
      if error {
         error.memory = NSError(domain: domain, code: code, userInfo: [:])
      }
      return nil
   }

   // Do more stuff...
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X