Jouer avec Swift, venant d'un arrière-plan Java, pourquoi voudriez-vous choisir un Struct au lieu d'une classe? On dirait que c'est la même chose, avec un Struct offrant moins de fonctionnalités. Pourquoi le choisir alors?
swift
class
struct
design-principles
bluedevil2k
la source
la source
Réponses:
Selon la très populaire conférence WWDC 2015 sur la programmation orientée protocole dans Swift ( vidéo , transcription ), Swift fournit un certain nombre de fonctionnalités qui rendent les structures meilleures que les classes dans de nombreuses circonstances.
Les structures sont préférables si elles sont relativement petites et copiables car la copie est bien plus sûre que d'avoir plusieurs références à la même instance comme cela se produit avec les classes. Ceci est particulièrement important lors du passage d'une variable à de nombreuses classes et / ou dans un environnement multithread. Si vous pouvez toujours envoyer une copie de votre variable à d'autres endroits, vous n'avez jamais à vous soucier que cet autre endroit change la valeur de votre variable sous vous.
Avec Structs, il est beaucoup moins nécessaire de s'inquiéter des fuites de mémoire ou des courses de plusieurs threads pour accéder / modifier une seule instance d'une variable. (Pour les plus techniques, l'exception à cela est lors de la capture d'une structure à l'intérieur d'une fermeture, car elle capture en fait une référence à l'instance, sauf si vous la marquez explicitement pour la copie).
Les classes peuvent également devenir gonflées car une classe ne peut hériter que d'une seule superclasse. Cela nous encourage à créer d'énormes superclasses qui englobent de nombreuses capacités différentes qui ne sont que vaguement liées. L'utilisation de protocoles, en particulier avec des extensions de protocole où vous pouvez fournir des implémentations aux protocoles, vous permet d'éliminer le besoin de classes pour obtenir ce type de comportement.
L'exposé présente ces scénarios où les classes sont préférées:
Cela implique que les structures devraient être la valeur par défaut et que les classes devraient être une solution de rechange.
D'un autre côté, la documentation du langage de programmation Swift est quelque peu contradictoire:
Ici, il prétend que nous devrions utiliser par défaut les classes et utiliser les structures uniquement dans des circonstances spécifiques. En fin de compte, vous devez comprendre l'implication réelle des types de valeur par rapport aux types de référence, puis vous pouvez prendre une décision éclairée sur le moment d'utiliser des structures ou des classes. Gardez également à l'esprit que ces concepts sont en constante évolution et que la documentation du langage de programmation rapide a été écrite avant la présentation de la programmation orientée protocole.
la source
In practice, this means that most custom data constructs should be classes, not structures.
Pouvez-vous m'expliquer comment, après avoir lu cela, vous obtenez que la plupart des ensembles de données devraient être des structures et non des classes? Ils ont donné un ensemble spécifique de règles quand quelque chose devrait être une structure et ont dit à peu près "tous les autres scénarios, une classe est meilleure".Étant donné que les instances de structure sont allouées sur la pile et les instances de classe sont allouées sur le tas, les structures peuvent parfois être considérablement plus rapides.
Cependant, vous devez toujours le mesurer vous-même et décider en fonction de votre cas d'utilisation unique.
Prenons l'exemple suivant, qui illustre 2 stratégies d'encapsulation
Int
de type de données à l'aide destruct
etclass
. J'utilise 10 valeurs répétées pour mieux refléter le monde réel, où vous avez plusieurs champs.La performance est mesurée en utilisant
Le code peut être trouvé à https://github.com/knguyen2708/StructVsClassPerformance
MISE À JOUR (27 mars 2018) :
Depuis Swift 4.0, Xcode 9.2, exécutant la version Release sur iPhone 6S, iOS 11.2.6, le paramètre du compilateur Swift est le suivant
-O -whole-module-optimization
:class
la version a pris 2,06 secondesstruct
la version a pris 4,17e-08 secondes (50 000 000 fois plus rapide)(Je ne fais plus la moyenne de plusieurs séries, car les écarts sont très faibles, moins de 5%)
Remarque : la différence est beaucoup moins dramatique sans optimisation du module entier. Je serais heureux si quelqu'un pouvait indiquer ce que fait réellement le drapeau.
MISE À JOUR (7 mai 2016) :
Depuis Swift 2.2.1, Xcode 7.3, exécutant la version Release sur iPhone 6S, iOS 9.3.1, en moyenne sur 5 exécutions, le paramètre du compilateur Swift est le suivant
-O -whole-module-optimization
:class
la version a pris 2.159942142sstruct
la version a pris 5,83E-08s (37 000 000 fois plus rapide)Remarque : comme quelqu'un a mentionné que dans les scénarios du monde réel, il y aurait probablement plus d'un champ dans une structure, j'ai ajouté des tests pour les structures / classes avec 10 champs au lieu de 1. Étonnamment, les résultats ne varient pas beaucoup.
RÉSULTATS ORIGINAUX (1er juin 2014):
(Exécuté sur struct / classe avec 1 champ, pas 10)
Depuis Swift 1.2, Xcode 6.3.2, exécutant la version Release sur iPhone 5S, iOS 8.3, en moyenne sur 5 exécutions
class
la version a pris 9.788332333sstruct
la version a pris 0.010532942s (900 fois plus rapide)ANCIENS RÉSULTATS (d'une heure inconnue)
(Exécuté sur struct / classe avec 1 champ, pas 10)
Avec la version construite sur mon MacBook Pro:
class
version a pris 1.10082 secstruct
version a pris 0.02324 sec (50 fois plus rapide)la source
Similitudes entre les structures et les classes.
J'ai créé l'essentiel pour cela avec des exemples simples. https://github.com/objc-swift/swift-classes-vs-structures
Et les différences
1. Héritage.
les structures ne peuvent pas hériter rapidement. Si tu veux
Allez en classe.
2. Passer
Les structures Swift passent par valeur et les instances de classe passent par référence.
Différences contextuelles
Constante de structure et variables
Exemple (utilisé lors de la WWDC 2014)
Définit une structure appelée Point.
Maintenant, si j'essaie de changer le x. C'est une expression valable.
Mais si je définissais un point comme constant.
Dans ce cas, le point entier est immuable constant.
Si j'ai utilisé une classe Point à la place, c'est une expression valide. Parce que dans une classe, la constante immuable est la référence à la classe elle-même et non à ses variables d'instance (sauf si ces variables sont définies comme constantes)
la source
Voici quelques autres raisons à considérer:
Les structures obtiennent un initialiseur automatique que vous n'avez pas du tout besoin de conserver dans le code.
Pour obtenir ceci dans une classe, vous devez ajouter l'initialiseur et maintenir l'intialiseur ...
Les types de collection de base comme
Array
les structures sont. Plus vous les utilisez dans votre propre code, plus vous vous habituerez à passer par valeur plutôt que par référence. Par exemple:Apparemment, l'immuabilité contre la mutabilité est un sujet énorme, mais beaucoup de gens intelligents pensent que l'immuabilité - les structures dans ce cas - est préférable. Objets mutables vs objets immuables
la source
internal
portée.mutating
si vous êtes explicite sur les fonctions qui changent leur état. Mais leur nature en tant que types de valeur est ce qui est important. Si vous déclarez une structure aveclet
vous ne pouvez pas appeler de fonctions de mutation dessus. La vidéo de la WWDC 15 sur Une meilleure programmation grâce aux types de valeur est une excellente ressource à ce sujet.En supposant que nous savons que Struct est un type de valeur et Class est un type de référence .
Si vous ne savez pas ce qu'est un type de valeur et un type de référence, voir Quelle est la différence entre le passage par référence et le passage par valeur?
Basé sur le post de mikeash :
Personnellement, je ne nomme pas mes cours comme ça. Je nomme généralement le mien UserManager au lieu de UserController mais l'idée est la même
De plus, n'utilisez pas de classe lorsque vous devez remplacer chaque instance d'une fonction, c'est-à-dire qu'elles n'ont aucune fonctionnalité partagée .
Donc, au lieu d'avoir plusieurs sous-classes d'une classe. Utilisez plusieurs structures conformes à un protocole.
Un autre cas raisonnable pour les structures est lorsque vous voulez faire un delta / diff de votre ancien et nouveau modèle. Avec les types de références, vous ne pouvez pas le faire hors de la boîte. Avec les types de valeur, les mutations ne sont pas partagées.
la source
Quelques avantages:
la source
La structure est beaucoup plus rapide que la classe. De plus, si vous avez besoin d'héritage, vous devez utiliser Class. Le point le plus important est que la classe est le type de référence tandis que la structure est le type de valeur. par exemple,
permet maintenant de créer une instance des deux.
laisse maintenant passer ces instances à deux fonctions qui modifient l'identifiant, la description, la destination etc.
aussi,
donc,
maintenant, si nous imprimons l'identifiant et la description du flightA, nous obtenons
Ici, nous pouvons voir que l'id et la description de FlightA sont modifiés parce que le paramètre passé à la méthode de modification pointe en fait vers l'adresse mémoire de l'objet flightA (type de référence).
maintenant, si nous imprimons l'id et la description de l'instance FLightB que nous obtenons,
Ici, nous pouvons voir que l'instance FlightB n'est pas modifiée car dans la méthode modifyFlight2, l'instance réelle de Flight2 est passée plutôt que référence (type de valeur).
la source
Here we can see that the FlightB instance is not changed
Structs
sontvalue type
etClasses
sontreference type
Utilisez un
value
type lorsque:Utilisez un
reference
type lorsque:De plus amples informations peuvent également être trouvées dans la documentation Apple
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
Information additionnelle
Les types de valeurs rapides sont conservés dans la pile. Dans un processus, chaque thread a son propre espace de pile, donc aucun autre thread ne pourra accéder directement à votre type de valeur. Par conséquent, aucune condition de concurrence critique, verrous, interblocages ou toute complexité de synchronisation de thread associée.
Les types de valeurs n'ont pas besoin d'allocation de mémoire dynamique ou de comptage de références, deux opérations coûteuses. En même temps, les méthodes sur les types de valeurs sont distribuées statiquement. Celles-ci créent un énorme avantage en faveur des types de valeur en termes de performances.
Pour rappel, voici une liste de Swift
Types de valeur:
Types de référence:
la source
Répondant à la question du point de vue des types de valeur par rapport aux types de référence, ce blog d'Apple semble très simple:
Comme mentionné dans cet article, une classe sans propriétés inscriptibles se comportera de manière identique avec une structure, avec (j'ajouterai) une mise en garde: les structures sont les meilleures pour les modèles thread-safe - une exigence de plus en plus imminente dans l'architecture d'application moderne.
la source
Avec les classes, vous obtenez l'héritage et êtes transmis par référence, les structures n'ont pas d'héritage et sont transmises par valeur.
Il y a de grandes sessions WWDC sur Swift, cette question spécifique est répondue en détail dans l'une d'entre elles. Assurez-vous de les regarder, car cela vous permettra de vous mettre à jour beaucoup plus rapidement que le guide des langues ou l'iBook.
la source
Je ne dirais pas que les structures offrent moins de fonctionnalités.
Bien sûr, le moi est immuable sauf dans une fonction de mutation, mais c'est tout.
L'héritage fonctionne bien tant que vous vous en tenez à la bonne vieille idée que chaque classe doit être abstraite ou finale.
Implémentez des classes abstraites en tant que protocoles et des classes finales en tant que structures.
La bonne chose à propos des structures est que vous pouvez rendre vos champs mutables sans créer d'état mutable partagé car la copie à l'écriture s'en charge :)
C'est pourquoi les propriétés / champs dans l'exemple suivant sont tous mutables, ce que je ne ferais pas en Java ou en C # ou dans les classes swift .
Exemple de structure d'héritage avec un peu d'utilisation sale et simple en bas dans la fonction nommée "exemple":
la source
Modèle de création:
En rapide, Struct est une valeur types qui est automatiquement cloné. Par conséquent, nous obtenons le comportement requis pour implémenter gratuitement le modèle de prototype.
Alors que les classes sont le type de référence, qui n'est pas automatiquement cloné lors de l'affectation. Pour implémenter le modèle prototype, les classes doivent adopter le
NSCopying
protocole.La copie superficielle duplique uniquement la référence, qui pointe vers ces objets, tandis que la copie profonde duplique la référence de l'objet.
L'implémentation de la copie profonde pour chaque type de référence est devenue une tâche fastidieuse. Si les classes incluent un type de référence supplémentaire, nous devons implémenter un modèle de prototype pour chacune des propriétés de référence. Et puis nous devons réellement copier le graphique d'objet entier en implémentant le
NSCopying
protocole.En utilisant des structures et des énumérations , nous avons simplifié notre code car nous n'avons pas à implémenter la logique de copie.
la source
De nombreuses API Cocoa nécessitent des sous-classes NSObject, ce qui vous oblige à utiliser la classe. Mais à part cela, vous pouvez utiliser les cas suivants du blog Swift d'Apple pour décider d'utiliser un type de valeur struct / enum ou un type de référence de classe.
https://developer.apple.com/swift/blog/?id=10
la source
Un point qui n'attire pas l'attention dans ces réponses est qu'une variable contenant une classe par rapport à une structure peut parfois
let
permettre des modifications sur les propriétés de l'objet, alors que vous ne pouvez pas le faire avec une structure.Ceci est utile si vous ne voulez pas que la variable pointe vers un autre objet, mais que vous devez quand même modifier l'objet, c'est-à-dire dans le cas de plusieurs variables d'instance que vous souhaitez mettre à jour l'une après l'autre. S'il s'agit d'une structure, vous devez permettre à la variable d'être réinitialisée sur un autre objet en utilisant complètement
var
pour ce faire, car un type de valeur constante dans Swift permet correctement la mutation zéro, tandis que les types de référence (classes) ne se comportent pas de cette façon.la source
Comme les structures sont des types de valeur et que vous pouvez créer très facilement la mémoire qui est stockée dans la pile, la structure peut être facilement accessible et après la portée du travail, elle est facilement désallouée de la mémoire de la pile via pop depuis le haut de la pile. D'un autre côté, la classe est un type de référence qui stocke en tas et les modifications apportées dans un objet de classe auront un impact sur l'autre objet car elles sont étroitement couplées et de type de référence.Tous les membres d'une structure sont publics alors que tous les membres d'une classe sont privés .
L'inconvénient de struct est qu'il ne peut pas être hérité.
la source
La structure et la classe sont des types de données définis par l'utilisateur
Par défaut, la structure est publique alors que la classe est privée
La classe implémente le principe de l'encapsulation
Les objets d'une classe sont créés sur la mémoire du tas
La classe est utilisée pour la réutilisabilité tandis que la structure est utilisée pour regrouper les données dans la même structure
Les membres de données de structure ne peuvent pas être initialisés directement mais ils peuvent être affectés par l'extérieur de la structure
Les membres de données de classe peuvent être initialisés directement par le constructeur sans paramètre et attribués par le constructeur paramétré
la source