(примечание: конкретный ответ 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 и т. Д.), С другой стороны, требуются чаще в реализациях и в целом более сложны.
удачи!