Comprendre l'immuabilité

13

Je suis nouveau dans la programmation orientée objet, et un concept qui m'a pris un certain temps à saisir est l'immuabilité. Je pense que l'ampoule s'est éteinte hier soir mais je veux vérifier:

Lorsque je rencontre des déclarations selon lesquelles un objet immuable ne peut pas être modifié, je suis perplexe car je peux, par exemple, faire ce qui suit:

NSString *myName = @"Bob";
myName = @"Mike";

Là, je viens de changer myName, de type immuable NSString. Mon problème est que le mot «objet» peut faire référence à l'objet physique en mémoire ou à l'abstraction «monNom». L'ancienne définition s'applique au concept d'immuabilité.

Quant à la variable, une définition plus claire (pour moi) de l'immuabilité est que le valeur d'un objet immuable ne peut être modifiée qu'en changeant également son emplacement dans la mémoire, c'est-à-dire sa référence (également connue sous le nom de pointeur).

Est-ce correct ou suis-je toujours perdu dans les bois?

Michael Mangold
la source
13
Votre type n'est pas un NSString, c'est un " pointeur vers et NSString", qui n'est pas immuable. Je ne sais rien de C objectif, mais je devine dans votre exemple qui @"Mike"crée une nouvelle instance de NSStringet en lui attribuant au pointeur , myName. Vous n'avez donc pas changé l'objet qui myNamepointait, juste ce qu'il pointait.
fwgx
2
@fwgx Mettez-le comme réponse et vous obtiendrez mon vote positif.
Gulshan
Merci à tous pour toutes les réponses, elles sont très utiles. Je comprends maintenant que l'objet immuable est la valeur en mémoire, pas la variable qui le pointe.
Michael Mangold
@Gulshan Done, voir ci-dessous ....
fwgx
Pour comprendre l'immuabilité, je recommande d'apprendre un langage de programmation qui sépare clairement la liaison (c'est-à-dire donner des noms aux objets) et la mutabilité (c'est-à-dire permettre à un nom d'être réaffecté à un autre objet). ML (sous toutes ses formes: SML, Ocaml, F #) en est un bon exemple.
Gilles 'SO- arrête d'être méchant'

Réponses:

4

Il semble que vous vous dirigiez dans la bonne direction, mais vous ne l'avez pas encore tout à fait. C'est faux:

Là, je viens de changer myName, de type immuable NSString. Mon problème est que le mot «objet» peut faire référence à l'objet physique en mémoire ou à l'abstraction «monNom».

Dans votre extrait de code, il myNamen'est pas de type immuable NSString, il est de type mutableNSString* (pointeur vers NSString). Il semble que la chose clé qui vous manque est de comprendre qu'un pointeur est juste une autre valeur , et qu'il a une vie complètement distincte de la chose qu'il pointe (ou des choses, si vous le changez en cours de vie).

Vous dites:

... la valeur d'un objet immuable ne peut être modifiée qu'en changeant également son emplacement dans la mémoire, c'est-à-dire sa référence (également appelée pointeur).

C'est faux. Un objet ne possède les pointeurs qui pointent vers elle, ni l'emplacement de la mémoire d'un objet contrôlé ou autrement affecté par des pointeurs vers elle.

Ainsi, les deux NSStringobjets de votre exemple ( @"Bob"et @"Mike") sont complètement séparés de la myNamevariable. Ils sont également complètement séparés les uns des autres. Lorsque vous changez myNamepour pointer vers @"Mike"au lieu de pointer vers @"Bob", vous ne modifiez pas les NSStringobjets.


Pour être complet, je noterai que les ramasse-miettes rendent cela plus complexe dans la mesure où les modifications apportées aux pointeurs peuvent affecter les objets vers lesquels ils pointent (ed). Cependant, il s'agit d'un détail d'implémentation qui ne devrait pas affecter le comportement observable du code.

John Bartholomew
la source
17

Vous êtes perdu aux mots. L'immuabilité signifie: Tant que vous ne modifiez pas la variable, elle «contiendra» toujours la même valeur, peu importe ce que vous faites avec les autres variables.

Contre-exemple en C (un peu simplifié, en supposant une architecture qui le permet):

 char *a = "Hello World";
 char *b = a;
 b[0] = 'Y';

Désormais, un "ne contient plus" (c'est-à-dire pointe vers) la chaîne "Hello World", mais c'est "Yello World" à la place.

Dans les langages où les chaînes sont immuables, comme Java et (EDIT: sûr) C #, vous ne pouvez pas faire cela. En aucune façon. Cela signifie que chaque partie du programme peut conserver en toute sécurité une référence à la chaîne et compter que son contenu ne change jamais; sinon, ils devraient créer une copie juste pour être sûr.

Mais la variable est toujours modifiable. Vous pouvez le laisser pointer vers un autre objet. C'est juste que l'objet lui-même ne changera pas derrière votre dos.

user281377
la source
1
Techniquement, vous pouvez modifier le contenu des chaînes en C #, il vous suffit d'utiliser du unsafecode.
Aaronaught
1
Aaronaught: Merci pour l'info, vous avez raison; pour tous ceux qui ont besoin de savoir comment: msdn.microsoft.com/en-us/library/ms228599.aspx
user281377
10

Vous confondez des variables avec des objets. Les variables peuvent être utilisées pour stocker des références à des objets, mais ce ne sont PAS des objets. Ce sont des objets qui sont immuables, pas des variables, vous pouvez donc changer la variable d'un objet à un autre, mais vous ne pouvez pas changer les attributs de l'objet s'il est immuable.

Considérez l'objet comme un voisin bruyant et ivre. S'il est raisonnable (mutable), vous pourriez peut-être frapper à sa porte et le convertir à un style de vie où il ne fait pas autant de bruit. Mais s'il est immuable, votre seul changement est d'espérer que quelqu'un d'autre emménage!

Kilian Foth
la source
3
J'ai eu des voisins que j'aurais aimé couper.
Dave Nay
Je ne pense pas que vous ayez besoin de votre exemple dans le deuxième paragraphe car votre explication est assez bonne. OMI, l'exemple pourrait simplement confondre. Cependant +1 pour une réponse succincte à la question du type.
Paul McCabe
@DaveNay: Je m'excuse pour le jeu de mots furtif. Ce n'était pas du tout destiné.
Kilian Foth
6

Une variable n'est pas un objet. Une variable est un nom, qui fait référence à un objet (ou plus généralement à une valeur).

Par exemple, "le gardien de but" est un nom que nous utilisons pour désigner l'objet (la personne) chargé de défendre le but. Mais si je remplace cette personne par une autre (parce que la première est blessée ou autre), la nouvelle personne est maintenant appelée le "gardien de but".

L'instruction d'affectation est ce qui rend les variables mutables (certains langages, comme Haskell ne l'ont pas et utilisent en fait des variables immuables). Il vous permet de redéfinir la signification d'un nom et ainsi de réaffecter la valeur.

Désormais, les objets eux - mêmes peuvent être immuables. Il y a quelques milliers d'années, on aurait pu penser que les diamants étaient immuables. Peu importe ce que vous avez fait avec un diamant, vous ne pouvez pas le modifier. Que vous l'appeliez waggawooga (traduit vaguement par "la plus grande pierre brillante de notre tribu") ou que vous ayez cessé de l'appeler ainsi (parce que vous en avez trouvé une plus grosse), le diamant est resté le même. En revanche, le morceau de bois que vous avez utilisé pour sculpter des images drôles avec votre waggawooga n'est pas resté le même. Cela s'est révélé mutable. Même s'il avait toujours le même nom.

Les variables et les valeurs peuvent être immuables (indépendamment). Dans ce cas, ce sont les objets qui sont immuables. Une fois que vous avez construit un NSString, vous ne pouvez plus le modifier. Vous pouvez l'appeler par des noms et le faire circuler, mais il restera le même. Contrairement à cela, NSMutableStringpeut être modifié après la création, par exemple en appelant la setStringméthode.

back2dos
la source
J'adore la comparaison waggawooga!
Michael K
Juste pour ne pas confondre l'affiche originale plus que nécessaire: bien que votre argument selon lequel une variable n'est pas un objet soit bon, une variable n'est pas réellement définie comme "un nom qui fait référence à une valeur". Une variable est éventuellement nommée et fait référence à un emplacement de stockage qui contient une valeur. Dans de nombreux langages de programmation, les variables n'ont pas besoin d'être nommées, et dans de nombreux langages, la même variable peut avoir plusieurs noms.
Eric Lippert
4

Je pense que vous vous sentez perdu, car vous mélangez deux concepts: l'objet lui-même et le nom de variable lié à cet objet.

Les objets immuables ne peuvent pas être modifiés. Période. Cependant, le nom de variable (symbole) lié à un objet immuable peut être modifié pour être lié à un autre objet immuable.

En d'autres termes, ce que vous avez fait dans ces deux lignes était:

  1. créer un objet chaîne immuable avec la valeur "Bob"
  2. lier le symbole myNameà cet objet
  3. créer un objet chaîne immuable avec la valeur "Mike"
  4. lier le symbole myNameà cet objet
vartec
la source
2

Votre type n'est pas un NSString, c'est un " pointeur vers un NSString", qui n'est pas immuable. Je ne sais rien de l'objectif C, mais je suppose que dans votre exemple, il @"Mike"s'agit de créer une nouvelle instance de NSStringet de l'attribuer au pointeur myName . Donc , vous n'avez pas changé l'objet qui myNamea été pointé à, juste ce qu'il pointait.

fwgx
la source
0

Voici un exemple d'objet mutable: un tableau de charen C:

char str[10];

Je peux changer le contenu de strmon contenu (tant qu'il ne dépasse pas 10 caractères). Pour qu'il soit reconnu comme une chaîne par les fonctions de bibliothèque de chaînes C, il doit y avoir un 0 à la fin, afin qu'il puisse contenir des chaînes jusqu'à 9 caractères:

strcpy(str, "hello"); // str now contains the string "hello\0"
strcpy(str, "world"); // str now contains the string "world\0"

str[0] = 'W';         // str now contains "World\0"

und so weiter .

Comparez cela avec un objet chaîne en Java:

String foo = "Hello";

L' instance de chaîne (le bloc de mémoire qui contient les caractères «H», «e», «l», «l» et «o») ne peut pas être modifiée; Je ne peux pas modifier le contenu de cette chaîne. Lorsque vous écrivez quelque chose comme

foo = foo + " World";

vous n'ajoutez pas "World" à la fin de l'instance "Hello"; vous créez une nouvelle instance , copiez "Hello World" et mettez fooà jour pour faire référence à cette nouvelle instance de chaîne.

foone contient pas lui-même l'instance de chaîne; il se réfère uniquement à cette instance (similaire à un pointeur en C ou Obj-C), d'où pourquoi des types comme String sont appelés types de référence.

John Bode
la source