web-dev-qa-db-fra.com

Lazy Var vs Let

Je souhaite utiliser l'initialisation paresseuse pour certaines de mes propriétés dans Swift. Mon code actuel ressemble à ceci:

lazy var fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

Le fait est qu'une fois que fontSize est défini, il ne changera JAMAIS. Je voulais donc faire quelque chose comme ça:

lazy let fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

Ce qui est impossible.

Seul cela fonctionne:

let fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

Donc - je veux une propriété qui sera chargée paresseusement mais ne changera jamais. Quelle est la bonne façon de procéder? en utilisant let et oublier l'init paresseux? Ou dois-je utiliser lazy var et oublier la nature constante de la propriété?

45
YogevSitton

Ceci est la dernière écriture de la Xcode 6.3 Beta/Swift 1.2 :

laissez les constantes ont été généralisées pour ne plus nécessiter d'initialisation immédiate. La nouvelle règle est qu'une constante let doit être initialisée avant utilisation (comme une var), et qu'elle ne peut être initialisée: pas réaffectée ou mutée après l'initialisation.

Cela permet des modèles comme:

let x: SomeThing
if condition {
    x = foo()
} else {
    x = bar()
}

use(x)

qui nécessitait auparavant l'utilisation d'une var, même si aucune mutation n'a lieu. (16181314)

Évidemment, vous n'étiez pas la seule personne frustrée par cela.

24
Chris Conover

Livre Swift a la note suivante :

Vous devez toujours déclarer une propriété paresseuse en tant que variable (avec le mot clé var), car sa valeur initiale peut ne pas être récupérée avant la fin de l'initialisation de l'instance. Les propriétés constantes doivent toujours avoir une valeur avant la fin de l'initialisation et ne peuvent donc pas être déclarées paresseuses.

Cela a du sens dans le contexte de l'implémentation du langage, car toutes les propriétés constantes stockées sont calculées avant la fin de l'initialisation d'un objet. Cela ne signifie pas que la sémantique de let aurait pu être modifiée lorsqu'elle était utilisée avec lazy, mais cela n'a pas été fait, donc var reste la seule option avec lazy à ce stade.

En ce qui concerne les deux choix que vous avez présentés, je déciderais entre eux en fonction de l'efficacité:

  • Si l'accès à la valeur d'une propriété se fait rarement et que le calcul initial est coûteux, j'utiliserais var lazy
  • Si la valeur est accessible dans plus de 20..30% des cas ou si elle est relativement peu coûteuse à calculer, j'utiliserais let

Remarque: j'optimiserais davantage votre code pour pousser le conditionnel dans l'initialiseur CGFloat:

let fontSize : CGFloat = CGFloat(someCase  ? 30 : 17)
22
dasblinkenlight

Comme le souligne dasblinkenlight, les propriétés paresseuses doivent toujours être déclarées en tant que variables dans Swift. Cependant, il est possible de rendre la propriété en lecture seule afin qu'elle ne puisse être mutée qu'à partir du fichier source dans lequel l'entité a été définie.

private(set) lazy var fontSize: CGFloat = {
    if someCase {
        return 30
    } else {
        return 17
    }
}()
10
sdduursma