NSMutableString как сохранить / скопировать
У меня есть несколько NSMutableString в моем приложении (почти 10-11); все определено как ivar/ свойство
@property (nonatomic, retain) NSMutableString *str1;
Я где-то читал, что лучше использовать "копию" для строк. Это правда? Если да, могу ли я просто заменить retain для копирования в моем приложении и удалить релиз в dealloc?
Должен ли я также рассмотреть некоторые другие вещи?
Кроме того, нормально ли иметь 10-11 NSMutableString в одном приложении... Я имею в виду с точки зрения использования памяти? У меня также есть 4-5 NSMutableDictionary в моем приложении. Пожалуйста, дайте мне знать, если это хорошо.
3 ответа
(примечание: ответ NSMutableString - это путь вниз)
У меня есть несколько NSMutableString в моем приложении (почти 10-11); все определено как ivar/ свойство
@property (nonatomic, retain) NSMutableString *str1;
Я где-то читал, что лучше использовать "копию" для строк. Это правда?
да, копирование 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;`
Вы можете использовать синтезированную реализацию по умолчанию.
но есть поворот при объявлении:
@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
, где это возможно)retain также обычно плохой дизайн - клиенты не будут знать, когда строка была (или изменяется) внешне изменена, если вы не добавите эквивалентный внешний код для поддержки этого.
в зависимости от того, как используется интерфейс, это также может привести к большому количеству ненужного копирования. бу.
хорошо - теперь мы уверены, что наше "улучшение" в большинстве случаев все еще плохая идея =) как мы можем улучшить дизайн?
кроме исключительных случаев тесно связанных объектов, вы должны объявить / реализовать свое свойство следующим образом:
@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 и т. д.), с другой стороны, требуются чаще в реализациях и в целом более сложны.
удачи!
Хотя ответ Taskinoor правильный, я бы хотел добавить немного больше объяснений.
Рекомендуется использовать copy для классов, которые являются частью кластера классов, которые имеют изменяемые / неизменяемые пары; такие как NSString
/NSMutableString
NSArray
/NSMutableArray
NSDictionary
/NSMutableDictionary
NSSet
/NSMutableSet
Причина этого заключается в том, что возможно иметь свойство, которое объявлено как неизменяемый тип (например, NSString), и передать ему изменяемый тип (например, NSMutableString). В этом случае можно изменить свойство снаружи класса, как описывает Taskinoor.
С помощью copy
рекомендуется, потому что он ведет себя разумно с кластерами классов. отправка copy
в изменяемый класс возвращает неизменную копию объекта (т.е. copy
сообщение для NSMutableString
возвращает NSString
). Но, отправка copy
неизменному коллеге эквивалентно отправке ему retain
сообщение.
Для изменяемой строки копирование и сохранение имеют разные результаты. Если вы сделаете копию str1 в str2, то любое изменение в str1 не будет отражаться в str2, а любое изменение в str2 не будет отражаться в str1, так как они являются отдельным объектом. Но если str1 сохраняется в str2, то оба ссылаются на один и тот же изменяемый строковый объект, и изменение через str1 отразится на str2. Но NSString не является изменчивым. Таким образом, для простой строки (то есть, не изменяемой строки) копия просто увеличивает счетчик ссылок, это означает, что она внутренне обрабатывается как сохранение (я не уверен на 100% в отношении обработки копии как сохранение для неизменяемых объектов, но видел это на ТАК в некотором другом вопросе. Гуглят, я буду редактировать, если смогу найти ссылку.).
И если вы используете copy, вам все равно нужно выпустить это в dealloc.
Наличие 10 - 11 строк нормального размера не должно отрицательно влиять на использование памяти. Типичное приложение может содержать гораздо больше строк. Под нормальным размером я подразумеваю, что вы не добавляете 1 миллион символов в строку. Это будет серьезной проблемой. Память, занятая строкой, будет увеличиваться по мере увеличения длины строки. То же самое касается словаря. Память, занятая словарем, будет зависеть от того, что вы храните в словаре. Сам словарь не имеет много накладных расходов. Вы всегда можете проверить использование памяти вашего приложения с помощью инструмента.