Эта настройка на самом деле делает некоторые очень интересные вещи и поднимает пару хороших моментов об управлении памятью Objective-C. Давайте сначала повторим код:
// Testing.h
@interface Testing : NSObject {
NSMutableString *retainString;
NSMutableString *copyString;
}
@property(nonatomic,retain) NSMutableString *retainString;
@property(nonatomic,copy) NSMutableString *copyString;
// Testing.m
@implementation Testing
@synthesize retainString, copyString;
- (id)init {
if(self = [super init]) {
NSMutableString *test = [[NSMutableString alloc] init];
NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
self.retainString = test;
NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
self.copyString = test;
NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
[self.copyString appendFormat:@"test"];
NSLog(@"test %d; retain %d; copy %d", [test retainCount], [retainString retainCount], [copyString retainCount]);
}
return self;
}
@end
Это приводит к выводу журнала:
2009-12-24 03:35:01.408 RetainCountTesting[1429:40b] test 1; retain 0; copy 0
2009-12-24 03:35:01.410 RetainCountTesting[1429:40b] test 2; retain 2; copy 0
2009-12-24 03:35:01.410 RetainCountTesting[1429:40b] test 2; retain 2; copy 2147483647
2009-12-24 03:35:01.413 RetainCountTesting[1429:40b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendFormat:'
Так что здесь происходит? Первые два звонка довольно просты:
- При первоначальном вызове
alloc
/ init
создается новый объект NSMutableString с сохранением количества 1, как и ожидалось. У нас есть один объект с одним удержанием на нем.
- Присвоение свойству
retain
ed увеличивает ожидаемый счетчик. У нас есть один объект с двумя удержаниями на нем.
Вот где это становится странным. Присвоение свойству copy
действительно делает копию, но не так, как вы ожидаете. NSString и NSMutableString являются частью того, что называется кластер классов - когда вы создаете или изменяете строку, она может быть или не быть экземпляром ожидаемого класса. Язык может мутировать его в какое-то другое закулисное представление.
В этом конкретном случае, когда копирование выполняется, очевидно, язык решает, что строка (так как она не содержит информации) должна считаться неизменной, и делает это так. Это часто наблюдается, когда люди делают что-то вроде [[NSString alloc] initWithString:@"hello"]
- это постоянная статическая строка, поэтому никакой динамический выделение объекта не требуется. Сохранение статичности помогает среде выполнения работать лучше.
Итак, теперь у нас есть два объекта: наш исходный объект test
, который был сохранен дважды, и новый объект, который является статическим и поэтому имеет счет сохранения INT_MAX
. Наконец, поскольку новая строка является неизменной, вызов метода-мутатора убивает программу.
Кроме того, изменение исходного вызова с init
на initWithString:
действительно приводит к тому, что назначение копирования выполняется (несколько), как и ожидалось - вы получаете только счет сохранения 1 для скопированного объекта, но вы все равно не можете мутировать его. Опять же, это, вероятно, связано с некоторой оптимизацией внутри компилятора, который решил, что строка является статической, и не видел причин делать ее изменяемой, если это не нужно.
Чтобы ответить на ваш последний вопрос: да , вы можете позвонить release
на любой из этих объектов. Это просто не поможет. В лучшем случае вы уничтожите скопированный объект (так как он имел счет сохранения 1); в худшем случае это ничего не даст статическому строковому объекту. Однако я бы порекомендовал продолжать работать со свойствами: вместо того, чтобы освобождать скопированный объект, почему бы просто не сделать self.copyString = nil;
? Так как он вызывает установщик свойств, он позаботится о выпуске по мере необходимости, и тогда у вас нет указателя на объект, все еще плавающий вокруг.
Для получения дополнительной информации обо всем этом, прочитайте: