J'ai une classe Person
simple dans Swift qui ressemble à ceci:
class Person {
var name = "John Doe"
var age = 18
var children = [Person]?
\\ init function goes here, but does not initialize children array
}
Au lieu de déclarer children
comme un tableau facultatif, je pourrais simplement le déclarer et l'initialiser comme un tableau vide comme ceci:
var children = [Person]()
J'essaie de décider quelle approche est la meilleure. Déclarer le tableau en tant que tableau facultatif signifie qu'il ne prendra pas du tout de mémoire, alors qu'un tableau vide dispose d'au moins de la mémoire qui lui est allouée, n'est-ce pas? Ainsi, l'utilisation du tableau en option signifie qu'il y aura au moins une économie de mémoire. Je suppose que ma première question est: y a-t-il vraiment une économie de mémoire réelle impliquée ici, ou mes hypothèses à ce sujet sont-elles incorrectes?
D'un autre côté, s'il est facultatif, à chaque fois que j'essaie de l'utiliser, je devrai vérifier s'il s'agit de nil
ou non avant d'y ajouter ou d'en supprimer des objets. Il y aura donc une certaine perte d'efficacité là-bas (mais pas beaucoup, j'imagine).
J'aime un peu l'approche facultative. Tous les Person
n'auront pas d'enfants, alors pourquoi ne pas laisser children
être nil
jusqu'à ce que le Person
décide de s'installer et d'élever une famille?
Quoi qu'il en soit, je voudrais savoir s'il y a d'autres avantages ou inconvénients spécifiques à l'une ou l'autre approche. C'est une question de conception qui reviendra encore et encore.
La possibilité de choisir entre un tableau vide ou facultatif nous donne la possibilité d'appliquer celui qui décrit le mieux les données d'un point de vue sémantique.
Je choisirais:
Permettez-moi de faire quelques exemples:
Notez que mon avis consiste uniquement à rendre le code plus clair et explicite - je ne pense pas qu'il y ait de différence significative en termes de performances, d'utilisation de la mémoire, etc. pour choisir une option ou l'autre.
Je vais faire le cas contraire de Yordi - un tableau vide dit aussi clairement que "cette personne n'a pas d'enfants", et vous fera économiser une tonne de tracas. children.isEmpty
est une vérification facile de l'existence des enfants, et vous n'aurez jamais à déballer ou à vous soucier d'un nil
inattendu.
En outre, comme note, déclarer quelque chose comme facultatif ne signifie pas qu'il faut zéro espace - c'est le .None
cas d'un Optional<Array<Person>>
.
Chose intéressante, nous avons récemment eu peu de discussions sur cette même question au travail.
Certains suggèrent qu'il existe de subtiles différences sémantiques. Par exemple. nil
signifie qu'une personne n'a aucun enfant, mais qu'est-ce que 0
signifier? Est-ce que cela signifie "a des enfants, tous 0" ? Comme je l'ai dit, la sémantique pure "a 0 enfant" et "n'a pas d'enfant" ne fait aucune différence lorsque vous travaillez avec ce modèle dans le code. Dans ce cas, pourquoi ne pas choisir une approche plus simple et moins prudente -? - y?
Certains suggèrent que la conservation d'un nil
peut indiquer que, par exemple, lors de la récupération du modèle depuis le backend, quelque chose s'est mal passé et nous avons eu une erreur au lieu des enfants. Mais je pense que le modèle ne devrait pas essayer d'avoir ce type de sémantique et nil
ne devrait pas être utilisé comme indication d'une erreur dans le passé.
Je pense personnellement que le modèle devrait être aussi stupide que possible et l'option la plus stupide dans ce cas est un tableau vide.
Avoir une option vous fera glisser ce ?
jusqu'à la fin des jours et utilisez guard let
, if let
ou ??
encore et encore.
Vous devrez avoir une logique de déballage supplémentaire pour l'implémentation de NSCoding
, vous devrez faire person.children?.count ?? 0
au lieu de simple person.children.count
lorsque vous affichez ce modèle dans n'importe quel contrôleur de vue.
Le but final de toute cette manipulation est d'afficher quelque chose sur l'interface utilisateur. Diriez-vous vraiment
"Cette personne n'a pas d'enfants" et "Cette personne a 0 enfants" pour nil
et un tableau vide en conséquence? J'espère que vous ne le feriez pas :)
Enfin, et c'est vraiment l'argument le plus fort que j'ai
subviews
de UIView
: c'est var subviews: [UIView] { get }
children
de SKNode
: c'est var children: [SKNode] { get }
Il y a des tonnes d'exemples comme celui-ci dans le framework Cocoa: IViewController :: childViewControllers et plus.
Même du monde pur Swift world: Dictionary :: keys bien que cela puisse être un peu exagéré).
Pourquoi est-il acceptable pour une personne d'avoir nil
enfants, mais pas pour SKNode
? Pour moi, l'analogie est parfaite. Hé, même le nom de la méthode de SKNode
est children
:)
Mon avis: il doit y avoir une raison évidente de garder ces tableaux en option, comme un très bon, sinon un tableau vide offre la même sémantique avec moins de déballage.
Enfin, quelques références à de très bons articles, chacun de ces
Dans le post de Natasha, vous trouverez un lien vers le blog de NSHipster et dans le paragraphe Swiftification vous pouvez lire ceci:
Par exemple, au lieu de marquer les valeurs de retour NSArray comme nullables, de nombreuses API ont été modifiées pour renvoyer un tableau vide - sémantiquement, elles ont la même valeur (c'est-à-dire rien), mais un tableau non facultatif est beaucoup plus simple à utiliser.
Parfois, il y a une différence entre quelque chose qui n'existe pas et qui est vide.
Disons que nous avons une application où un utilisateur peut modifier une liste de numéros de téléphone et nous enregistrons ces modifications sous la forme modifiedPhoneNumberList
. Si aucune modification ne s'est jamais produite, le tableau doit être nul. Si l'utilisateur a modifié les nombres analysés en les supprimant, tout le tableau doit être vide.
Vide signifie que nous allons supprimer tous les numéros de téléphone existants, nil signifie que nous conservons tous les numéros de téléphone existants. La différence est importante ici.
Lorsque nous ne pouvons pas faire la différence entre une propriété vide ou inexistante ou peu importe vide, c'est la voie à suivre. Si un Person
devait perdre son seul enfant, nous devrions simplement supprimer cet enfant et avoir un tableau vide plutôt que de vérifier si le count
est 1, puis définir l'ensemble du tableau sur nil
.
J'utilise toujours des tableaux vides.
À mon humble avis, le but le plus important des options dans Swift est d'encapsuler en toute sécurité une valeur qui peut être nulle. Un tableau agit déjà comme ce type de wrapper - vous pouvez demander au tableau s'il a quelque chose à l'intérieur et accéder à sa valeur en toute sécurité avec pour les boucles, le mappage, etc. Avons-nous besoin de mettre un wrapper dans un wrapper? Je ne pense pas.
Swift est conçu pour tirer parti des valeurs optionnelles et du déballage optionnel.
Vous pourriez déclarer également le tableau nul, car cela vous fera économiser une très petite quantité de mémoire (presque non perceptible) .
J'irais avec un tableau facultatif au lieu d'un tableau qui représente une valeur nulle pour garder les modèles de conception de Swift heureux :)
Je pense aussi
if let children = children {
}
semble plus agréable que:
if(children != nil){
}