Pourquoi copie-t-on les NSString ?
Neofelis
Membre
Dans mes livres objective-c on utilise la plupart du temps copy dans les setters plutôt que retain pour les NSString. Quelle en est la raison ?
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
- avec retain, tu retiens la référence: tu as donc un seul et même objet
- avec copy, tu copies la string: tu as donc 2 objets différents
Dans le 1er cas, toute modification implique tout le monde, pas dans le second
On copie normalement les variables d'instances de type "value" => Ce qui t'intéresse n'est pas l'objet lui-même (le pointeur vers la zone mémoire), mais sa valeur. Du coup, si un objet quelconque change la valeur de l'objet, c'est mauvais. Du fait, en utilisant une copie, tu évite ce soucis. (ceci est vrai pour toutes tes "values" de ta couche donnée). Par opposition, pour les "relationships", ce qui t'interesse est l'adresse de l'objet, les valeurs de ses variables d'instance pouvant êtres modifiées par d'autres objets => On retient.
Ca c'est pour le principe. Mais dans les faits, on avait d'ailleurs eu une discussion là -dessus relative aux accesseurs, une NSString, un NSData, une NSDate ... n'étant pas modifiables a priori, il n'y a pas de raison particulière de les copier, donc la retenir peut suffire.
Personnellement, je n'écris plus mes accesseurs (@property, @synthetize sauf cas particuliers), mais la doc d'Apple indique http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAccessorMethods.html
Sauf que pour les objets immutables, -[copy] est implémenté par un -[retain].
Dixit le livre Cocoa Design Patterns.
Ce qui parait cohérent puisqu'il n'y a aucune intérêt à avoir exactement le même objet en mémoire s'il ne peut pas être modifié.
Ou alors aussi pour la même raison, dans un accesseur qui reçoit un mutable pour le fixer en non mutable.
J'ai du mal à comprendre dans ce cas pourquoi on dit dans mon bouquin que le type "copy" (pour les properties) est adapté pour les NSString... ???
Un peu paumé pour le coup...
J'en ai plusieurs mais je crois que c'est "Objective-C 2.0 : Le Guide de survie" de Pejvan Beigui. Par contre je n'arrive plus à retrouver le passage en question après recherche ce matin... (de là à dire que j'ai rêvé... ^^)
Ceci dit j'ai rencontré une autre étrangeté hier soir en faisant des tests. J'ai créé un objet NSString et affiché ensuite son retainCount qui m'affiche 4294967295 (qui correspond à UINT_MAX)... Après une petite recherche sur le net et d'après ce que j'ai compris c'est que les objets de NSString sont des objets statiques et le fait de "retenir" ou "libérer" ce type d'objet n'a pas de sens... du coup ça sert à rien de faire des retain et release dans les setters de NSString ? C'est pas très clair tout ça...
- Par exemple [tt]NSString* stat = @Chaà®ne statique;[/tt] étant une chaà®ne statique, constante, connue à la compilation, sera compilée en dur dans le code (un "retain" ou un "release" dessus n'a aucun effet, c'est là le cas un peu particulier)
- Alors que [tt]NSString* dyn = [NSString stringWithFormat:@Chaà®ne dynamique %d !,val];[/tt] est une chaà®ne qui sera créé dynamiquement (forcément, vu que là on peut pas prédire sa valeur à la compilation) et donc pour le coup le retain et le release sont nécessaires et feront le boulot attendu.
NSString est loin d'être la classe la plus adéquate pour comprendre les mécanisme sous-jacents d'Objective-C comme la gestion mémoire, tellement il peut y avoir de cas particulier avec elle (le fait que ce soit un class cluster, le fait que les chaà®nes peuvent être statiquement allouées...).
En pratique tu n'as pas à te soucier de tout ça normalement : tu utilises NSString de façon "intuitive" et classique comme n'importe quelle autre NSObject, tu n'as pas besoin de savoir que c'est un class cluster, si ta chaà®ne est statique ou dynamique, ou quoi : tu lui appliques les règles de gestion mémoire classique et c'est tout (c'est là la beauté du modèle de gestion mémoire ObjC, également).
Mais sous le capot cela fait partie des classes qui sont le plus optimisées et gérant des cas bien particuliers (car le langage ObjC lui-même te permet de créer des NSString directement avec la syntaxe [tt]@xxx[/tt], ce "@" est une notation incluse dans le langage), du coup c'est loin d'être l'exemple le plus simple pour comprendre comment ça marche vu toutes les subtilités qu'il y a avec cette classe en interne.
Donc, pour répondre à ta question : OUI il faut faire les retain et release dans les setters de NSString, car c'est nécessaire si ta NSString est allouée dynamiquement (genre stringWithFormat). Et ça ne fait pas de mal si elle est allouée statiquement donc autant uniformiser le code (d'autant que tu ne peux pas savoir par code si tu es dans un cas où dans l'autre et qu'il n'y aurait aucun intérêt à faire la distinction de toute façon).
Si tu veux faire des tests et des essais à regarder le retainCount (qui ne doit servir qu'à des fins de debug de toute façon) et autre pour mieux comprendre ce qui se passe sous le capôt de Cocoa, je te conseille de prendre autre chose que NSString pour faire tes observations (par exemple crée une classe perso dérivant de NSObject), car NSString est un cas bien trop particulier et spécifique.