Подтвердить что ты не робот

NSMutableString как сохранить/скопировать

У меня есть номер NSMutableString в моем приложении (почти 10-11); все определены как ivar/property

@property (nonatomic, retain) NSMutableString *str1;

Я где-то читал, что лучше использовать "копию" для строк. Это правда? Если да, могу ли я просто заменить сохранение для копирования в своем приложении и удалить выпуск в dealloc?

Нужно ли мне также рассмотреть некоторые другие вещи?

Кроме того, нормально ли иметь 10-11 NSMutableString в 1 приложении. Я имею в виду использование памяти? У меня также есть 4-5 NSMutableDictionary, а также в моем приложении. Пожалуйста, дайте мне знать, если все в порядке.

4b9b3361

Ответ 1

(примечание: конкретный ответ NSMutableString - это путь вниз)

У меня есть номер NSMutableString в моем приложении (почти 10-11); все определены как ivar/property

@property (nonatomic, retain) NSMutableString *str1;Я где-то читал, что лучше использовать "копию" для строк. Это правда?

да, копирование NSString (не говоря уже о NSMutableString еще) должно быть по умолчанию. причина (как утверждается другими плакатами) заключается в том, что реализация неизменяемого экземпляра может просто retain сама в реализации copyWithZone:. Кроме того, copy дает ожидаемое поведение. другими словами, нет смысла сохранять строку, если она на самом деле неизменяема. чтобы гарантировать, что вы имеете дело с неизменяемой строкой, вы просто копируете при создании и настройке.

Если да, могу ли я просто заменить сохранение на копирование в моем приложении и удалить выпуск в dealloc?

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

  • Для продолжительности программы существуют литералы CF/NS-String

  • вы можете использовать CF-API для создания строк, которые никогда не выпускаются или управляются за пределами традиционных механизмов сохранения/выпуска

  • Подклассы NS-Type могут реализовывать другую схему (хотя обычные соображения этого являются ошибочными)

Нужно ли мне также рассмотреть некоторые другие вещи?

теперь мы переходим к деталям типа NSMutable. реализация синтаксиса свойств вызывает copy. как известно, -[NSMutableString copy] возвращает неизменяемый NSString. это проблема, ожидающая появления наивной реализации, поскольку ваш NAR файл NSMutableString теперь является NSString, используя стандартизованную реализацию сеттера.

@property (nonatomic, retain) NSMutableString *str1;`
// or
@property (nonatomic, copy) NSMutableString *str1;`

при объявлении:

 @property (nonatomic, retain) NSMutableString *str1;`

вы можете использовать синтезированную реализацию по умолчанию.

но есть твист при объявлении:

 @property (nonatomic, copy) NSMutableString *str1;`

вам не следует использовать синтезированную реализацию по умолчанию. вместо этого вы должны написать свой собственный:

- (void)setStr1:(NSMutableString *)arg
{
    /* locking/observing/undo/etc omitted */
    NSMutableString * copy = [arg mutableCopy];
    NSMutableString * prev = str1;
    str1 = copy;
    [prev release];
}

- (NSMutableString *)str1
{
  /* locking/observing/etc omitted */
  /* don't return something the clients thinks they may
     possibly modify, and don't return something you may
     modify behind their back
  */
    return [[str1 mutableCopy] autorelease];
  // -- or???
    return [[str1 retain] autorelease];
}

хм... это не очень понятно и не очень важно для клиентов. это также подразумевает много ненужных вещей и вводит дополнительные накладные расходы. давайте переоценить это.

дополнительные соображения/проблемы на данный момент:

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

  • NSMutableString также является неоднозначным getter - см. реализацию. он копирует + autorelease или сохраняет + autorelease? нет стандартного ожидания для общей формы. Кроме того, клиенты могут зависеть от одного конкретного поведения. это действительно деталь реализации класса, если вы не имеете дело с плотно связанными объектами. тесно связанными объектами являются те, которые в основном являются зависимыми друг от друга, и обычно не могут использоваться повторно в других контекстах. эта концепция прекрасна, вы можете просто использовать частный класс по нескольким причинам. в любом случае семантика копирования/записи внешним клиентам и подклассам не является (неявно) понятной, а реализация опасна.

  • совместное использование NSMutableString обычно плохой дизайн. NSMutableString не является потокобезопасным. для клиентов не имеет никакого доступа к доступу и мутации строки - это опасно. любой дизайн, который разделяет объекты, которые не являются потокобезопасными, следует рассматривать с осторожностью. разделение в этом случае также распространяется на использование подкласса (поэтому сделайте его @private, где это возможно)

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

  • в зависимости от того, как используется интерфейс, это также может привести к большому количеству ненужного копирования. бух.

ok - теперь мы довольно убеждены, что наше "улучшение" по-прежнему является плохой идеей в большинстве случаев =), как мы можем улучшить дизайн?

вы должны, помимо исключительных случаев тесно связанных объектов, объявить/реализовать свое свойство следующим образом:

@interface MONThing : NSObject
{
@private
    NSMutableString * str1;
}

/* note: passes/returns NSString */
@property (nonatomic, copy) NSString * str1;

@end

@implementation MONThing

// no need for clients to create a mutableCopy
- (void)setStr1:(NSString *)arg
{
  /* locking/observing/undo/etc omitted */
    NSMutableString * copy = [arg mutableCopy];
    NSMutableString * prev = str1;
    str1 = copy;
    [prev release];
}

// the result is clearly defined. return a copy for
// thread-safety, expected behavior, and to minimize
// further copies when handling the result.
- (NSString *)str1
{
  /* locking/observing/etc omitted */
  /* don't return something the clients thinks they
     may possibly modify, and don't return something
     you may modify behind their back
  */
    return [[str1 copy] autorelease];
}

@end

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

вы также можете удалить общедоступные средства доступа, если они вам не нужны.

мы можем жить с этим интерфейсом, где это необходимо.

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

как упоминалось ранее, NSMutableString не является потокобезопасным, во многих случаях проще и эффективнее использовать (скопированный) NSString ivar, когда ваша строка мала или не изменяется часто. добавив изменяемую строку к интерфейсу (как указано выше), вы должны будете гарантировать безопасность потока вручную. во многих случаях оптимально делать что-то в этом направлении:

@interface MONThing : NSObject
{
    NSString * str1;
}

@property (nonatomic, copy) NSString * str1;

@end

@implementation MONThing

@synthesize str1;

- (void)updateTimeElapsed:(NSTimeInterval)seconds
{
    NSMutableString * s = [NSMutableString stringWithFormat:@"%f seconds", seconds];
    /* ...some mutations... */
    self.str1 = s;
}

@end

конечно, будет несколько исключений из этого - там, где вам понадобится mutable ivar, но лучше всего использовать неизменные ivars, где это возможно, и где сомневайтесь, вместо того, чтобы вводить тонну сложности с потоками. в большинстве случаев вы можете удалить использование изменяемых строк из интерфейсов и при необходимости построить строки (как показано выше).

коллекции (NSMutableArray, NSMutableDictionary, NSMutableSet и т.д.), с другой стороны, чаще всего требуются в реализациях и являются более сложными в целом.

Удачи!

Ответ 2

Хотя ответ Taskinoor верен, я хотел бы добавить немного больше объяснений.

Рекомендуется использовать копию для классов, которые являются частью кластера классов, которые имеют изменяемые/неизменяемые пары; например NSString/NSMutableString NSArray/NSMutableArray NSDictionary/NSMutableDictionary NSSet/NSMutableSet

Причиной этого является то, что возможно иметь свойство, объявленное как неизменяемый тип (например, NSString), но передающее ему изменяемый тип (например, NSMutableString). В этом случае можно изменить свойство вне класса, как описывает Taskinoor.

Рекомендуется использовать copy, потому что он ведет себя разумно с кластерами классов. отправка copy в изменяемый класс возвращает неизменяемую копию объекта (т.е. отправка сообщения copy в NSMutableString возвращает NSString)). Но отправка copy в неизменяемую копию эквивалентна отправке сообщения retain.

Ответ 3

Для изменяемой строки copy и сохранения имеют другой результат. Если вы сделаете копию str1 на str2, любое изменение на str1 не отразится на str2, и любое изменение в str2 не будет отображаться в str1, так как они являются отдельным объектом. Но если str1 сохраняется в str2, то оба ссылаются на один и тот же изменяемый строковый объект, а изменение через str1 будет отображаться в str2. Но NSString не изменяет. Таким образом, для простой строки (то есть не изменяемой строки) копия просто увеличивает счетчик ссылок, что означает, что она внутренне обрабатывается как сохранение (я не уверен на 100% относительно обработки копии, как сохранение для не изменяемых объектов, но видел это на SO в каком-то другом вопросе. Googling, я отредактирую, если найду ссылку.).

И если вы используете копию, вам все равно нужно освободить ее в dealloc.

Наличие 10 - 11 строк нормального размера не должно отрицательно влиять на использование памяти. Типичное приложение может содержать гораздо больше строк. По нормальному размеру я подразумеваю, что вы не добавляете 1 миллион символов в строку. Это будет серьезной проблемой. Память, взятая строкой, будет увеличиваться по мере увеличения длины строки. То же самое касается словаря. Память, занимаемая словарем, будет зависеть от того, что вы храните в словаре. Сам словарь не имеет больших накладных расходов. Вы всегда можете проверить использование памяти в своем приложении с помощью инструмента.