web-dev-qa-db-fra.com

Propriété NSString: copier ou conserver?

Disons que j'ai une classe appelée SomeClass avec un nom de propriété string:

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

Je comprends que ce nom peut recevoir un NSMutableString, auquel cas cela peut conduire à un comportement errant.

  • Pour les chaînes en général, est-ce toujours une bonne idée d'utiliser l'attribut copy au lieu de retain?
  • Une propriété "copiée" est-elle en aucun cas moins efficace qu'une telle propriété "retenue"?
328
PlagueHammer

Pour les attributs dont le type est une classe de valeurs immuables conforme au protocole NSCopying, vous devez presque toujours spécifier copy dans votre déclaration @property. Spécifier retain est quelque chose que vous ne voudrez presque jamais dans une telle situation.

Voici pourquoi vous voulez faire cela:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

La valeur actuelle de la propriété Person.name sera différente selon que la propriété est déclarée retain ou copy - ce sera @"Debajit" si la propriété est marquée retain, mais @"Chris" si la propriété est marquée copy.

Puisque dans presque tous les cas, vous voulez empêcher de muter les attributs d'un objet derrière son dos, vous devez marquer les propriétés qui les représentent copy. (Et si vous écrivez vous-même le setter au lieu d'utiliser @synthesize, vous devez vous rappeler d'utiliser réellement copy au lieu de retain.)

440
Chris Hanson

Copier doit être utilisé pour NSString. Si c'est Mutable, alors il est copié. Si ce n'est pas le cas, il est simplement retenu. Exactement la sémantique que vous voulez dans une application (laissez le type faire ce qui est le mieux).

120
Frank Krueger

Pour les chaînes en général, est-ce toujours une bonne idée d'utiliser l'attribut de copie au lieu de conserver?

Oui - en général, utilisez toujours l'attribut copy.

En effet, votre propriété NSString peut être transmise à une instance NSString ou une instance NSMutableString , et nous ne pouvons donc pas vraiment déterminer si la valeur transmise est un objet immuable ou mutable.

Une propriété "copiée" est-elle en aucun cas moins efficace qu'une telle propriété "retenue"?

  • Si votre propriété est passée à une instance de NSString , la réponse est "Non" - copie n'est pas moins efficace que retenir.
    (Ce n'est pas moins efficace, car NSString est suffisamment intelligent pour ne pas effectuer de copie.)

  • Si votre propriété reçoit une instance NSMutableString , la réponse est "Yes" - la copie est effectuée. moins efficace que retenir.
    (C'est moins efficace, car une allocation de mémoire et une copie doivent avoir lieu, mais c'est probablement une chose souhaitable.)

  • D'une manière générale, une propriété "copiée" a le potentiel d'être moins efficace. Cependant, grâce à l'utilisation du protocole NSCopying, il est possible d'implémenter une classe "tout aussi efficace" à copier qu'à conserver. Les instances de NSString en sont un exemple.

Généralement (pas seulement pour NSString), quand devrais-je utiliser "copier" au lieu de "conserver"?

Vous devez toujours utiliser copy lorsque vous ne souhaitez pas que l'état interne de la propriété change sans préavis. Même pour les objets immuables - les objets immuables correctement écrits gèreront efficacement la copie (voir la section suivante sur l'immutabilité et NSCopying).

Il peut y avoir des raisons de performances pour retain objets, mais cela vient avec une surcharge de maintenance: vous devez gérer la possibilité que l'état interne change en dehors de votre code. Comme on dit - optimisez dernier.

Mais j’ai écrit que ma classe était immuable - je ne peux pas simplement la "retenir"?

Non - utilisez copy. Si votre classe est vraiment immuable, il est préférable d'appliquer le protocole NSCopying pour que votre classe se retourne d'elle-même lorsque copy est utilisé. Si tu fais ça:

  • Les autres utilisateurs de votre classe en tireront des avantages en utilisant copy.
  • L'annotation copy rend votre propre code plus facile à gérer - l'annotation copy indique que vous n'avez vraiment pas besoin de vous inquiéter du changement d'état de cet objet ailleurs.
66
TJez

J'essaie de suivre cette règle simple:

  • Est-ce que je veux conserver le valeur de l'objet au moment où je l'assigne à ma propriété? Utilisez copie.

  • Est-ce que je veux conserver les objet et je me moque de ce que ses valeurs internes sont ou seront dans le futur? Utilisez fort (conserver).

Pour illustrer: Dois-je conserver la copie name "Lisa Miller" () ) ou je veux conserver sur le personne Lisa Miller ( fort )? Son nom pourrait changer plus tard en "Lisa Smith", mais elle sera toujours la même personne.

39

À travers cet exemple, copier et conserver peut être expliqué comme suit:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

si la propriété est de type copy alors,

une nouvelle copie sera créée pour la chaîne [Person name] qui contiendra le contenu de la chaîne someName. Désormais, toute opération sur la chaîne someName n'aura aucun effet sur [Person name].

[Person name] et someName les chaînes auront des adresses mémoire différentes.

Mais en cas de retenue,

le [Person name] contiendra la même adresse mémoire que celle de quelque chaîne, seul le nombre de conservations de la chaîne sera incrémenté de 1.

Ainsi, tout changement dans une chaîne de nom sera reflété dans la chaîne [Person name].

13
Divya Arora

Il est certain que mettre "copie" sur une déclaration de propriété va à l'encontre de l'utilisation d'un environnement orienté objet dans lequel les objets du tas sont passés par référence. L'un des avantages que vous obtenez ici est que, lorsque vous modifiez un objet, toutes les références à cet objet voir les dernières modifications. Un grand nombre de langages fournissent des mots-clés 'ref' ou similaires pour permettre aux types de valeur (c'est-à-dire les structures de la pile) de bénéficier du même comportement. Personnellement, j'utilisais une copie avec parcimonie. Si je pensais qu'une valeur de propriété devait être protégée des modifications apportées à l'objet à partir duquel elle avait été affectée, je pourrais appeler la méthode de copie de cet objet lors de l'affectation, par exemple:

p.name = [someName copy];

Bien sûr, lors de la conception de l'objet contenant cette propriété, vous seul saurez si la conception bénéficie d'un modèle dans lequel les affectations sont copiées - Cocoawithlove.com indique ce qui suit:

"Vous devez utiliser un accesseur de copie lorsque le paramètre setter peut être mutable mais vous ne pouvez pas faire modifier l'état interne d'une propriété sans avertissement " - alors le jugement quant à savoir si vous pouvez supporter la valeur de changer de manière inattendue est tout à vous. Imaginez ce scénario:

//person object has details of an individual you're assigning to a contact list.

Contact *contact = [[[Contact alloc] init] autorelease];
contact.name = person.name;

//person changes name
[[person name] setString:@"new name"];
//now both person.name and contact.name are in sync.

Dans ce cas, sans utiliser copy, notre objet contact prend automatiquement la nouvelle valeur. si nous l'utilisions, nous devions nous assurer manuellement que les modifications étaient détectées et synchronisées. Dans ce cas, conserver la sémantique pourrait être souhaitable; dans un autre, la copie pourrait être plus appropriée.

3
Clarkeye
@interface TTItem : NSObject    
@property (nonatomic, copy) NSString *name;
@end

{
    TTItem *item = [[TTItem alloc] init];    
    NSString *test1 = [NSString stringWithFormat:@"%d / %@", 1, @"Go go go"];  
    item.name = test1;  
    NSLog(@"-item.name: point = %p, content = %@; test1 = %p", item.name, item.name, test1);  
    test1 = [NSString stringWithFormat:@"%d / %@", 2, @"Back back back"];  
    NSLog(@"+item.name: point = %p, content = %@, test1 = %p", item.name, item.name, test1);
}

Log:  
    -item.name: point = 0x9a805a0, content = 1 / Go go go; test1 = 0x9a805a0  
    +item.name: point = 0x9a805a0, content = 1 / Go go go, test1 = 0x9a84660
1
len

Vous devez utiliser copier tout le temps pour déclarer la propriété NSString

@property (nonatomic, copy) NSString* name;

Vous devriez les lire pour plus d'informations sur le renvoi d'une chaîne immuable (dans le cas où une chaîne modifiable a été transmise) ou d'une chaîne conservée (dans le cas où une chaîne immuable a été transmise).

Référence du protocole NSCopying

Implémentez NSCopying en conservant l'original au lieu de créer une nouvelle copie lorsque la classe et son contenu sont immuables

objets de valeur

Donc, pour notre version immuable, nous pouvons simplement faire ceci:

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}
0
onmyway133