it-swarm.com.ru

NSString свойство: скопировать или сохранить?

Допустим, у меня есть класс с именем SomeClass с именем свойства string:

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

Я понимаю, что имени может быть присвоено NSMutableString, и в этом случае это может привести к ошибочному поведению.

  • Для строк в целом: всегда хорошая ли идея использовать атрибут copy вместо retain?
  • Является ли "скопированное" свойство каким-либо образом менее эффективным, чем такое "сохраняемое" свойство?
327
PlagueHammer

Для атрибутов, тип которых является классом неизменяемых значений, который соответствует протоколу NSCopying, вы почти всегда должны указывать copy в своем объявлении @property. Указание retain - это то, что вы почти никогда не хотите в такой ситуации.

Вот почему вы хотите сделать это:

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

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

[someName setString:@"Debajit"];

Текущее значение свойства Person.name будет отличаться в зависимости от того, объявлено ли свойство retain или copy - это будет @"Debajit", если свойство помечено retain, но @"Chris", если свойство помечено copy.

Поскольку почти во всех случаях вы хотите предотвратить поменять атрибуты объекта за его спиной, вы должны пометить свойства, представляющие их copy. (И если вы пишете установщик самостоятельно, а не используете @synthesize, вы должны помнить, чтобы фактически использовать copy вместо retain в нем.)

439
Chris Hanson

Копировать должен использоваться для NSString. Если он изменчив, то он копируется. Если это не так, то это просто сохраняется. Именно семантика, которую вы хотите в приложении (пусть тип делает то, что лучше).

120
Frank Krueger

Для строк в целом всегда ли полезно использовать атрибут copy вместо retain?

Да - обычно всегда используйте атрибут копирования.

Это потому, что вашему свойству NSString можно передать экземпляр NSString или экземпляр NSMutableString , и поэтому мы не можем определить, действительно ли передаваемое значение является неизменным или изменяемым объектом.

Является ли "скопированное" свойство каким-либо образом менее эффективным, чем такое "сохраняемое" свойство?

  • Если вашей собственности передается экземпляр NSString , ответ "Нет" - копирование не менее эффективно, чем сохранить.
    (Это не менее эффективно, потому что NSString достаточно умен, чтобы фактически не выполнять копирование.)

  • Если вашему свойству передан экземпляр NSMutableString , тогда ответ "Да" - копирование менее эффективно, чем сохранить.
    (Это менее эффективно, потому что должно произойти фактическое выделение памяти и копирование, но это, вероятно, желательно.)

  • Вообще говоря, "скопированное" свойство потенциально может быть менее эффективным - однако с помощью протокола NSCopying можно реализовать класс, который "столь же эффективен" для копирования, как и для сохранения. NSString экземпляры являются примером этого.

Вообще (не только для NSString), когда я должен использовать "copy" вместо "retain"?

Вы всегда должны использовать copy, если вы не хотите, чтобы внутреннее состояние свойства изменялось без предупреждения. Даже для неизменяемых объектов - правильно написанные неизменяемые объекты будут эффективно обрабатывать копии (см. Следующий раздел об неизменяемости и NSCopying).

Для объектов retain могут быть причины производительности, но это связано с дополнительными затратами на обслуживание - вы должны управлять возможностью изменения внутреннего состояния вне вашего кода. Как говорится - оптимизируй последний.

Но я написал свой класс, чтобы он был неизменным - я не могу просто "сохранить" его?

Нет - используйте copy. Если ваш класс действительно неизменяемый, то лучше всего реализовать протокол NSCopying, чтобы ваш класс возвращал сам себя, когда используется copy. Если вы делаете это:

  • Другие пользователи вашего класса получат преимущества в производительности, если они будут использовать copy.
  • Аннотация copy делает ваш собственный код более понятным - аннотация copy указывает на то, что вам действительно не нужно беспокоиться об изменении состояния этого объекта в другом месте.
66
TJez

Я стараюсь следовать этому простому правилу:

  • Хочу ли я удерживать значение объекта в тот момент, когда я его назначаю моей собственности? Используйте копия.

  • Хочу ли я удерживать объект и мне все равно, каковы его внутренние значения в настоящее время или будет в будущем? Используйте сильный (сохранить).

Чтобы проиллюстрировать: хочу ли я удерживать имя "Лиза Миллер" ( копию ) или я хочу удержать на человек Лиза Миллер ( сильная )? Позже ее имя может измениться на "Лиза Смит", но она все равно останется тем же человеком.

39
Johannes Fahrenkrug

С помощью этого примера можно объяснить, как скопировать и сохранить, например:

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

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

[someName setString:@"Debajit"];

если свойство имеет тип copy, то

для строки [Person name] будет создана новая копия, которая будет содержать содержимое строки someName. Теперь любая операция со строкой someName не будет влиять на [Person name].

Строки [Person name] и someName будут иметь разные адреса памяти.

Но в случае сохранения,

оба [Person name] будут содержать тот же адрес памяти, что и строка соменам, просто число сохраненных строк соменам будет увеличено на 1.

Таким образом, любое изменение в строке somename будет отражено в строке [Person name].

13
Divya Arora

Безусловно, добавление "copy" в объявление свойства противоречит использованию объектно-ориентированной среды, в которой объекты в куче передаются по ссылке - одно из преимуществ, которые вы получаете, заключается в том, что при изменении объекта все ссылки на этот объект увидеть последние изменения. Многие языки предоставляют "ref" или подобные ключевые слова, чтобы позволить типам значений (то есть структурам в стеке) получить выгоду от того же поведения. Лично я бы использовал копирование экономно, и если бы я чувствовал, что значение свойства должно быть защищено от изменений, внесенных в объект, которому он был назначен, я мог бы вызвать метод копирования этого объекта во время назначения, например:

p.name = [someName copy];

Конечно, при проектировании объекта, содержащего это свойство, только вы будете знать, выиграет ли проект от шаблона, в котором назначения получают копии - Cocoawithlove.com может сказать следующее:

"Вы должны использовать средство доступа к копии, когда параметр setter может быть изменяемым , но вы не можете изменить внутреннее состояние свойства без предупреждения " - так суждение о том, сможете ли вы вынести ценность неожиданного изменения, принадлежит вам самому. Представьте себе этот сценарий:

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

В этом случае, без использования копирования, наш контактный объект автоматически принимает новое значение; если бы мы использовали его, нам бы пришлось вручную убедиться, что изменения были обнаружены и синхронизированы. В этом случае сохранение семантики может быть желательным; в другом случае копия может быть более подходящей.

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

Вы должны использовать copy все время для объявления свойства NSString

@property (nonatomic, copy) NSString* name;

Вы должны прочитать их для получения дополнительной информации о том, возвращает ли она неизменяемую строку (в случае, если изменяемая строка была передана) или возвращает сохраненную строку (в случае, если неизменная строка была передана)

NSCopying Protocol Reference

Реализуйте NSCopying, сохраняя оригинал вместо создания новой копии, когда класс и его содержимое неизменны

объекты-значения

Итак, для нашей неизменной версии мы можем просто сделать это:

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