Propriedade NSString: copiar ou reter?

Digamos que eu tenha uma class chamada SomeClass com um nome de propriedade de string :

 @interface SomeClass : NSObject { NSString* name; } @property (nonatomic, retain) NSString* name; @end 

Eu entendo que o nome pode ser atribuído a um NSMutableString , caso em que isso pode levar a um comportamento errôneo.

  • Para strings em geral, é sempre uma boa ideia usar o atributo copy vez de retain ?
  • Uma propriedade “copiada” é, de alguma forma, menos eficiente do que uma propriedade “retida”?

Para atributos cujo tipo é uma class de valor imutável que está em conformidade com o protocolo NSCopying , você quase sempre deve especificar a copy na sua declaração @property . Especificar a retain é algo que você quase nunca quer em tal situação.

Veja por que você quer fazer isso:

 NSMutableString *someName = [NSMutableString stringWithString:@"Chris"]; Person *p = [[[Person alloc] init] autorelease]; p.name = someName; [someName setString:@"Debajit"]; 

O valor atual da propriedade Person.name será diferente dependendo se a propriedade é declarada como retain ou copy – será @"Debajit" se a propriedade estiver marcada como retain , mas @"Chris" se a propriedade estiver marcada como copy .

Como em quase todos os casos você deseja impedir a mutação dos atributos de um object, você deve marcar as propriedades que os representam. (E se você escrever o setter você mesmo ao invés de usar @synthesize você deve se lembrar de usar a copy invés de retain nela.)

Cópia deve ser usada para NSString. Se é mutável, então é copiado. Se não for, então fica retido. Exatamente a semântica que você quer em um aplicativo (deixe o tipo fazer o que é melhor).

Para strings em geral, é sempre uma boa ideia usar o atributo copy em vez de manter?

Sim – em geral, sempre use o atributo de cópia.

Isso ocorre porque sua propriedade NSString pode receber uma instância NSString ou uma ocorrência NSMutableString e, portanto, não podemos realmente determinar se o valor que está sendo passado é um object imutável ou mutável.

Uma propriedade “copiada” é, de alguma forma, menos eficiente do que uma propriedade “retida”?

  • Se sua propriedade está sendo passada uma instância NSString , a resposta é ” Não ” – a cópia não é menos eficiente do que manter.
    (Não é menos eficiente porque o NSString é inteligente o suficiente para não realizar uma cópia.)

  • Se sua propriedade tiver passado por uma instância NSMutableString , a resposta será ” Sim ” – a cópia é menos eficiente do que reter.
    (É menos eficiente porque uma alocação de memory real e uma cópia devem ocorrer, mas isso é provavelmente uma coisa desejável.)

  • De um modo geral, uma propriedade “copiada” tem o potencial de ser menos eficiente – no entanto, através do uso do protocolo NSCopying , é possível implementar uma class que seja “tão eficiente” para copiar quanto para reter. As instâncias de NSString são um exemplo disso.

Geralmente (não apenas para NSString), quando devo usar “copiar” em vez de “reter”?

Você deve sempre usar a copy quando não quiser que o estado interno da propriedade seja alterado sem aviso. Mesmo para objects imutáveis ​​- objects imutáveis ​​escritos apropriadamente manipularão cópia de forma eficiente (veja a próxima seção sobre imutabilidade e NSCopying ).

Pode haver razões de desempenho para retain objects, mas isso vem com uma sobrecarga de manutenção – você deve gerenciar a possibilidade de o estado interno mudar fora do seu código. Como se costuma dizer – otimizar por último.

Mas eu escrevi que minha aula é imutável – não posso simplesmente “reter”?

Não – use copy . Se sua class é realmente imutável, então é uma boa prática implementar o protocolo NSCopying para fazer com que sua class retorne quando a copy for usada. Se você fizer isto:

  • Outros usuários da sua turma obterão os benefícios de desempenho quando usarem a copy .
  • A anotação de copy torna seu próprio código mais sustentável – a anotação de copy indica que você realmente não precisa se preocupar com esse estado de alteração de object em outro lugar.

Eu tento seguir esta regra simples:

  • Eu quero manter o valor do object no momento em que estou atribuindo-o à minha propriedade? Use cópia .

  • Eu quero me apegar ao object e não me importo com o que seus valores internos são atualmente ou serão no futuro? Use forte (reter).

Para ilustrar: Eu quero manter o nome “Lisa Miller” ( cópia ) ou quero segurar a pessoa Lisa Miller ( forte )? O nome dela pode mudar para “Lisa Smith” mais tarde, mas ela ainda será a mesma pessoa.

Através deste exemplo, copiar e reter pode ser explicado como:

 NSMutableString *someName = [NSMutableString stringWithString:@"Chris"]; Person *p = [[[Person alloc] init] autorelease]; p.name = someName; [someName setString:@"Debajit"]; 

se a propriedade for do tipo copy,

uma nova cópia será criada para a string [Person name] que conterá o conteúdo da string someName . Agora, qualquer operação na string someName não terá efeito em [Person name] .

[Person name] e someName cadeias terão endereços de memory diferentes.

Mas no caso de reter,

tanto o [Person name] manterá o mesmo endereço de memory quanto a string de nome do usuário, apenas a contagem de retenção da string de nome do usuário será incrementada em 1.

Portanto, qualquer alteração na string de nome do usuário será refletida na string [Person name] .

Certamente, colocar ‘copy’ em uma declaração de propriedade é contra o uso de um ambiente orientado a object onde os objects no heap são passados ​​por referência – um dos benefícios que você obtém aqui é que, ao alterar um object, todas as referências a esse object veja as últimas alterações. Muitas linguagens fornecem palavras-chave “ref” ou similares para permitir que tipos de valor (isto é, estruturas na pilha) se beneficiem do mesmo comportamento. Pessoalmente, eu usaria cópia com moderação, e se eu sentisse que um valor de propriedade deveria ser protegido de alterações feitas no object do qual ele foi atribuído, eu poderia chamar o método de cópia desse object durante a atribuição, por exemplo:

 p.name = [someName copy]; 

É claro que, ao projetar o object que contém essa propriedade, somente você saberá se o design se beneficia de um padrão em que as atribuições tiram cópias – o Cocoawithlove.com tem o seguinte para dizer:

“Você deve usar um acessador de cópia quando o parâmetro setter pode ser mutável, mas você não pode alterar o estado interno de uma propriedade sem avisar ” – assim, o julgamento sobre se você pode suportar o valor para alterar inesperadamente é todo seu. Imagine este cenário:

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

Nesse caso, sem usar a cópia, nosso object de contato aceita o novo valor automaticamente; se o usássemos, porém, precisaríamos ter certeza de que as alterações foram detectadas e sincronizadas. Nesse caso, reter a semântica pode ser desejável; em outro, a cópia pode ser mais apropriada.

 @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 

Você deve usar a cópia o tempo todo para declarar a propriedade NSString

 @property (nonatomic, copy) NSString* name; 

Você deve ler estes para mais informações sobre se ele retorna uma string imutável (no caso de uma string mutável foi passada) ou retorna uma string retida (no caso de uma string imutável ser passada)

Referência do protocolo NSCopying

Implemente o NSCopying mantendo o original em vez de criar uma nova cópia quando a class e seu conteúdo forem imutáveis

Objetos de valor

Então, para a nossa versão imutável, podemos fazer isso:

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

Se a string for muito grande, a cópia afetará o desempenho e duas cópias da string grande usarão mais memory.

Como o nome é um NSString (imutável), copiar ou reter não faz diferença se você definir outro NSString para nomear. Em outra palavra, a cópia se comporta como reter, aumentando a contagem de referência em um. Eu acho que é uma otimização automática para classs imutáveis, já que elas são imutáveis ​​e não precisam ser clonadas. Mas quando um mstr NSMutalbeString é definido para o nome, o conteúdo de mstr será copiado por uma questão de correção.