NSMutableString как сохранить / скопировать - PullRequest
17 голосов
/ 14 февраля 2011

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

@property (nonatomic, retain) NSMutableString *str1;

Я где-то читал, что лучше использовать «copy» для строк.Это правда?Если да, могу ли я просто заменить retain для копирования в мое приложение и удалить релиз в dealloc?

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

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

Ответы [ 3 ]

27 голосов
/ 15 февраля 2011

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

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

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

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

27 голосов
/ 15 февраля 2011

(примечание: конкретный ответ NSMutableString не подходит)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 @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 также является неоднозначным получателем - см. Реализацию.копирует + авто-релиз или сохраняет + авто-релиз?нет стандартного ожидания для общей формы.Кроме того, клиенты могут зависеть от одного конкретного поведения.это действительно деталь реализации класса, если только вы не имеете дело с тесно связанными объектами.К тесно связанным объектам относятся те, которые в основном являются взаимозависимыми и не могут повторно использоваться в других контекстах.эта концепция в порядке, вы можете просто использовать закрытый класс по нескольким причинам.в любом случае семантика копирования / записи для внешних клиентов и подклассов (неявно) не ясна, а реализация опасна.

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

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

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

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

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

@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 в качестве ивара, где это возможно.

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

@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

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

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

удачи!

12 голосов
/ 14 февраля 2011

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

А если вы используете копирование, вам все равно нужно выпустить его в dealloc.

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

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