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

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

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

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

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

Ответ 1

Для атрибутов, тип которых является неизменяемым классом значений, который соответствует протоколу 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" if свойство отмечено copy.

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

Ответ 2

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

Ответ 3

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

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

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

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

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

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

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

Как правило (не только для NSString), когда следует использовать "copy" вместо "сохранить"?

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

В этом примере копирование и сохранение можно объяснить следующим образом:

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] будут хранить тот же адрес памяти, что и для строки somename, только значение count of somename будет увеличено на 1.

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

Ответ 6

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

p.name = [someName copy];

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

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

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

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

Ответ 7

@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

Ответ 8

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

@property (nonatomic, copy) NSString* name;

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

Ссылка на протокол NSCopying

Внедрить NSCopying, сохранив оригинал вместо создания новая копия, когда класс и его содержимое являются неизменными

Объекты ценности

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

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

Ответ 9

Если строка очень большая, то копия будет влиять на производительность, а две копии большой строки будут использовать больше памяти.

Ответ 10

Поскольку имя является (неизменным) NSString, копирование или сохранение не имеет значения, если вы укажете другое имя NSString. Другими словами, копирование ведет себя так же, как и сохранение, увеличивая количество ссылок на единицу. Я думаю, что это автоматическая оптимизация для неизменяемых классов, поскольку они неизменяемы и не требуют клонирования. Но когда a NSMutalbeString mstr задано для имени, содержимое mstr будет скопировано для правильности.