Quelle est la différence entre cela:
_ = navigationController?.popViewController(animated: true)
defer {
let rootVC = navigationController?.topViewController as? RootViewVC
rootVC?.openLink(url: url)
}
return
et ça:
_ = navigationController?.popViewController(animated: true)
let rootVC = navigationController?.topViewController as? RootViewVC
rootVC?.openLink(url: url)
return
Apple Swift dit: "Vous utilisez une instruction defer pour exécuter un ensemble d'instructions juste avant que l'exécution du code ne quitte le bloc de code actuel.", Mais je ne comprends toujours pas tout à fait.
Quelle est la différence entre une déclaration différée et une déclaration juste avant le retour?
Toute la différence dans le monde. L'instruction defer
est exécutée après le retour! Cela vous permet d'accomplir des choses qui ne peuvent être accomplies d'aucune autre manière.
Par exemple, vous pouvez renvoyer une valeur et alors changer la valeur. Apple utilise cette astuce assez régulièrement; voici, par exemple, le code de la documentation de la séquence montrant comment écrire une séquence personnalisée:
struct Countdown: Sequence, IteratorProtocol {
var count: Int
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}
Si vous avez écrit cela comme
count -= 1
return count
... il se briserait; nous ne voulons pas décrémenter count
puis le retourner, nous voulons retourner count
puis le décrémenter.
En outre, comme cela a déjà été souligné, l'instruction defer
est exécutée, peu importe comment vous quittez. Et cela fonctionne, peu importe que vous quittiez le scope actuel, ce qui pourrait ne pas impliquer du tout return
; defer
fonctionne pour un corps de fonction, un bloc while, une construction if, un bloc do, etc. Un seul return
n'est pas le seul moyen de quitter une telle portée! Il peut y avoir plus d'un return
dans votre méthode, et/ou vous pouvez lancer une erreur, et/ou vous pouvez avoir un break
, etc. etc., ou vous pouvez simplement atteindre le la dernière ligne du champ d'application naturellement; defer
est exécuté dans tous les cas possibles. Écrire le même code "à la main", afin de couvrir toutes les sorties possibles, peut être très sujet aux erreurs.
Dans votre exemple, il n'y a en fait aucune différence, mais regardez ceci:
func foo(url: URL) -> Int
let fileDescriptor : CInt = open(url.path, O_EVTONLY);
defer {
close(fileDescriptor)
}
guard let bar = something1() else { return 1 }
guard let baz = something2() else { return 2 }
doSomethingElse(bar, baz)
return 3
}
close(fileDescriptor)
est toujours exécutée quelle que soit la ligne dans laquelle la fonction retourne.
l'instruction defer est utilisée pour exécuter un morceau de code exactement avant que l'exécution ne quitte la portée récente.
Par exemple:
func defer() {
print("Beginning")
var value: String?
defer {
if let v = value {
print("Ending execution of \(v)")
}
}
value = "defer function"
print("Ending")
}
La première ligne qui s'imprimera est: Début
La deuxième ligne qui s'imprimera est: Fin
Et la dernière ligne qui s'imprimera est: Fin de l'exécution de la fonction de report.
L'utilisation de defer
vous permet d'éviter le nettoyage conditionnel à la fin de la fonction.
Considérez cet exemple:
class Demo {
var a : String
init(_ a:String) {
self.a = a
}
func finish() {
print("Finishing \(a)")
}
}
func play(_ n:Int) {
let x = Demo("x")
defer { x.finish() }
if (n < 2) {return}
let y = Demo("y")
defer { y.finish() }
if (n < 3) {return}
let z = Demo("z")
defer { z.finish() }
}
play(1)
play(2)
play(3)
La fonction play
crée un, deux ou trois objets Demo
selon son paramètre et appelle finish
sur eux à la fin de l'exécution. Si la fonction revient du milieu, les instructions defer
ne sont pas exécutées et finish
n'est pas appelée pour les objets qui ne sont jamais créés.
Une alternative à cela nécessiterait l'utilisation d'options:
func play(_ n:Int) {
var x:Demo? = nil
var y:Demo? = nil
var z:Demo? = nil
x = Demo("x")
if (n >= 2) {
y = Demo("y")
}
if (n >= 3) {
z = Demo("z")
}
x?.finish()
y?.finish()
z?.finish()
}
Cette approche place toutes les déclarations en haut et vous oblige à déballer les options ultérieurement. Le code avec defer
, d'autre part, vous permet d'écrire du code de nettoyage près du code qui effectue l'initialisation.