NSString свойство: скопировать или сохранить? - PullRequest
326 голосов
/ 23 декабря 2008

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

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

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

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

Ответы [ 10 ]

438 голосов
/ 23 декабря 2008

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

120 голосов
/ 23 декабря 2008

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

66 голосов
/ 23 мая 2012

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

13 голосов
/ 20 сентября 2012

В этом примере копирование и сохранение можно объяснить как:

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

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

3 голосов
/ 22 мая 2012

Надлежащее размещение «копии» в объявлении свойства бросает вызов использованию объектно-ориентированной среды, в которой объекты в куче передаются по ссылке - одно из преимуществ, которые вы получаете, заключается в том, что при изменении объекта все ссылки к этому объекту смотрите последние изменения. Многие языки предоставляют «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.

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

1 голос
/ 07 марта 2014
@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
0 голосов
/ 17 января 2015

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

0 голосов
/ 29 мая 2014

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

@property (nonatomic, copy) NSString* name;

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

NSCopying Protocol Reference

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

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

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

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}
0 голосов
/ 06 августа 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...