Après avoir installé la version bêta de Xcode 7 et converti mon code Swift en Swift 2, j'ai eu un problème avec le code que je ne pouvais pas comprendre. Je sais que Swift 2 est nouveau, alors je cherche et découvre, puisqu'il n'y a rien à ce sujet, je devrais écrire une question.
Voici l'erreur:
L'appel peut être lancé, mais il n'est pas marqué avec 'try' et l'erreur n'est pas gérée
Code:
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 _ {
}
}
Snapshot:
Vous devez capturer l'erreur exactement comme vous le faites déjà pour votre appel save()
et comme vous gérez plusieurs erreurs ici, vous pouvez try
plusieurs appels de manière séquentielle dans un seul bloc d'interception, comme alors:
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, comme @ bames53 l’a souligné dans les commentaires ci-dessous, il est souvent préférable de ne pas intercepter l’erreur où elle a été jetée. Vous pouvez marquer la méthode comme throws
puis try
pour appeler la méthode. Par exemple:
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()
}
Lorsque vous appelez une fonction déclarée avec throws
dans Swift, vous devez annoter le site d'appel de fonction avec try
ou try!
. Par exemple, étant donné une fonction de projection:
func willOnlyThrowIfTrue(value: Bool) throws {
if value { throw someError }
}
cette fonction peut s'appeler comme:
func foo(value: Bool) throws {
try willOnlyThrowIfTrue(value)
}
Nous annotons ici l'appel avec try
, qui appelle le lecteur que cette fonction peut générer une exception et que les lignes de code suivantes risquent de ne pas être exécutées. Nous devons également annoter cette fonction avec throws
, car cette fonction peut lever une exception (c'est-à-dire, lorsque willOnlyThrowIfTrue()
lève, alors foo
renverra automatiquement l'exception vers le haut.
Si vous voulez appeler une fonction déclarée comme pouvant être lancée, mais que vous savez ne lancera pas votre cas parce que vous lui donnez une entrée correcte, vous pouvez utiliser try!
.
func bar() {
try! willOnlyThrowIfTrue(false)
}
Ainsi, lorsque vous garantissez que le code ne sera pas lancé, vous n'avez pas besoin d'ajouter du code standard supplémentaire pour désactiver la propagation des exceptions.
try!
est appliqué au moment de l'exécution: si vous utilisez try!
et que la fonction est lancée, l'exécution de votre programme sera interrompue avec une erreur d'exécution.
La plupart des codes de traitement des exceptions doivent ressembler à ce qui est décrit ci-dessus: soit vous propagez simplement les exceptions à la hausse, soit vous définissez des conditions telles que les exceptions possibles sont exclues. Tout nettoyage d’autres ressources de votre code doit se faire via la destruction d’objets (par exemple, deinit()
), ou parfois via le code 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.
}
Si, pour quelque raison que ce soit, vous avez un code de nettoyage à exécuter mais ne faisant pas partie d'une fonction deinit()
, vous pouvez utiliser 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 plupart des codes traitant des exceptions se contentent de les propager vers le haut, en effectuant un nettoyage en cours via deinit()
ou defer
. En effet, la plupart du code ne sait pas quoi faire des erreurs; il sait ce qui ne va pas, mais il ne dispose pas d'assez d'informations sur ce qu'un code de niveau supérieur tente de faire pour savoir quoi faire à propos de l'erreur. Il ne sait pas si présenter un dialogue à l'utilisateur est approprié, s'il doit réessayer ou si quelque chose d'autre est approprié.
Toutefois, le code de niveau supérieur doit savoir exactement quoi faire en cas d'erreur. Les exceptions permettent donc aux erreurs spécifiques de remonter de leur lieu d'origine à l'endroit où elles peuvent être traitées.
Le traitement des exceptions se fait via les instructions catch
.
func quux(value: Bool) {
do {
try willOnlyThrowIfTrue(value)
} catch {
// handle error
}
}
Vous pouvez avoir plusieurs instructions catch, chacune décrochant un type d'exception différent.
do {
try someFunctionThatThowsDifferentExceptions()
} catch MyErrorType.errorA {
// handle errorA
} catch MyErrorType.errorB {
// handle errorB
} catch {
// handle other errors
}
Pour plus de détails sur les meilleures pratiques avec exceptions, voir http://exceptionsafecode.com/ . Il est spécifiquement destiné au C++, mais après avoir examiné le modèle d'exception Swift, je pense que les bases s'appliquent également à Swift.
Pour plus de détails sur le modèle de syntaxe et de traitement des erreurs Swift, voir le livre . Le Swift Langage de programmation (version préliminaire Swift 2) .