web-dev-qa-db-fra.com

Quelle est la différence entre une déclaration différée et une déclaration juste avant le retour?

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.

16
Li Fumin

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.

33
matt

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.

16
vadian

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.

7
H S Progr

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.

4
dasblinkenlight