web-dev-qa-db-fra.com

Tableau facultatif vs tableau vide dans Swift

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.

35
Aaron Rasmussen

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:

  • Un tableau vide si la liste peut être vide, mais c'est un statut transitoire et à la fin il doit avoir au moins un élément. Le fait d'être non facultatif indique clairement que le tableau ne doit pas être vide
  • Facultatif s'il est possible que la liste soit vide pour tout le cycle de vie de l'entité conteneur. Être facultatif montre clairement que le tableau peut être vide

Permettez-moi de faire quelques exemples:

  • Bon de commande avec maître et détails (un détail par produit): un bon de commande peut avoir 0 détails, mais c'est un statut transitoire, car il ne serait pas logique d'avoir un bon de commande avec 0 produit
  • Personne avec enfants: une personne ne peut pas avoir d'enfants pendant toute sa vie. Ce n'est pas un statut transitoire (bien qu'il ne soit pas également permanent), mais en utilisant une option, il est clair qu'il est légitime pour une personne de ne pas avoir d'enfants.

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.

16
Antonio

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>>.

20
Nate Cook

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 :)

Dernière paille

Enfin, et c'est vraiment l'argument le plus fort que j'ai

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.

La dernière dernière paille

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.

14
i4niac

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.

5
Declan McKenna

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.

3
Trev14

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){

}
1
Yordi