iPhone: это утечка или нет
Недавно кто-то из Stack Overflow сказал мне, что приведенный ниже код не дает утечки, что свойство обрабатывает саму задержку:
self.locationManager = [[CLLocationManager alloc] init];
в dealloc:
self.locationManager = nil;
где в файле.h:
@property (nonatomic, retain) CLLocationManager *locationManager;
Я думал, что это была очевидная утечка, и полагал, что это должно исправить утечку:
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
Однако он утверждал, что это не сработает, потому что, по его словам, "вы не выпускаете автоматически свойства класса. Автоматически созданный метод доступа к свойству, определенному для сохранения, будет обрабатывать сохранение автоматически".
И он заставил меня задуматься, не прав ли он или я вообще не понял управление памятью?
РЕДАКТИРОВАТЬ 1: это код
self.myName=[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"];
отличается от
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
управление памятью?
Парень говорит, что первое правильно и отказывается от второго. Почему второй будет так НЕПРАВИЛЬНО? Насколько я вижу, оба присваивают автоматически освобожденные экземпляры некоторым свойствам, но почему-то все еще есть упрямый аргумент, что второй неверен. Я не вижу этого, любая помощь будет так приветствоваться.
7 ответов
Подсчет сохранений и выпусков помогает в этом случае. Это определенно утечка. Ваш locationManager
объект будет сохранен 2 раза: один раз alloc/init
звонки и один раз на имущество. Установка свойства в nil
выпустит только locationManager
один раз.
Для примеров, приведенных в Edit 1, они действительно одинаковы. Похоже, что другой разработчик испытывает отвращение к немедленному автоматическому выпуску или не совсем понимает, что autorelease
делает.
Семантика опции сохранения свойства:
- старое значение (если есть) получает сообщение о выпуске
- новое значение получает сообщение сохранения
Следовательно, ваш экземпляр CLLocationManager будет иметь счет сохранения 2 после установщика. Один из alloc и один из сохраняющего сеттера. Вы должны отправить сообщение об освобождении сразу после установщика:
CLLocationMamnager *aLocationManager = [[CLLocationManager alloc] init];
self.locationManager = aLocationManager;
[aLocationManager release];
В качестве альтернативы, добавьте его в пул авто-релиза, чтобы он был, по крайней мере, в конечном итоге бесплатным. Как ты сам написал
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
Более того, не используйте опцию сохранения свойства. Оставьте его как назначить (по умолчанию), и вы готовы к работе, так как вы все равно используете сохраненный объект.
Следующее утверждение сохраняется дважды, и как таковое должно быть выпущено дважды:
self.locationManager = [[CLLocationManager alloc] init];
Это, вероятно, лучше всего написать следующим образом:
self.locationManager = [[[CLLocationManager alloc] init]autorelease];
Теперь эта переменная была сохранена только один раз, и вы можете освободить ее в функции dealloc вашего класса.
Кроме того, если вы запускаете следующую строку кода, релиз называется:
locationManager = nil;
Поскольку locationManager синтезируется, когда вы устанавливаете его в ноль, он освобождается первым.
Кроме того, если вы сделали следующее, locationManager сначала будет выпущен, а затем сброшен за кулисы:
self.locationManager = foo;
Наконец, следующее произойдет сбой с exc_bad_access, потому что вы дважды освобождаете locationManager, когда вы устанавливаете его в foo:
self.locationManager = [[[CLLocationManager alloc] init]autorelease];
[locationManager release];
self.locationManager = foo;
Простое практическое правило: если свойство помечено как "сохранить", всегда присваивайте ему автоматически выпускаемую переменную (если вы ее создаете), если, конечно, вам также НЕ НУЖНО сохранить его в другом месте.
@jnic: вы удивительно неправы. Насколько я понимаю, предыдущему значению свойства отправляется сообщение о выпуске, а не объект, который вы хотите присвоить свойству. так что, да, предлагаемый код действительно пропадает, и вы должны отправить сообщение об автоматическом выпуске, как вы и думали, Ахмет Эмра
Добавьте эту строку под... Это, очевидно, дает ваш счет сохранения для locationManager. Это скажет вам, если вам нужно выпустить его вручную или нет.
NSLog(@"retainCount:%d", [locationManager retainCount]);
редактировать:
[locationManager release];
Хорошо, вот сделка.
Когда вы определяете свойство как...
@property (nonAtomic, retain) NSString myName;
... из-за значений по умолчанию команды свойства фактически это определяется как:
@property (nonAtomic, readwrite, retain, getter=getMyName,setter=setMyName) NSString myName;
Когда вы используете @synthesize myName;
За кулисами компилятор генерирует метод получения, который выглядит примерно так:
-(void) setMyName:(NSString *) aString{
if (!(myString==aString) { //test if a string is the same **object** as the current myString
if (aString != nil) { // if nil we don't want to send a retain
[aString retain]; // increment the retain count by one
}
[myString release]; //decrement the retain count of the currently assigned string by one.
myString=nil; //set the pointer to nil to ensure we don't point to the old string object
myString=aString; //assign the newly retained object to the myString symbol
}
}
Как вы можете видеть, любая строка из любого источника, с любым предыдущим счетом сохранения, автоматически освобожденным или нет, будет автоматически сохраняться методом при назначении, а когда назначается новое значение, он автоматически освобождается методом. Многократные назначения не будут составлять счет удержания. Пока вы используете сгенерированный сеттер, назначенный объект (в данном случае aString) всегда будет иметь счет сохранения, который будет поддерживать его в классе.
Вот почему вы можете сделать это...
self.myName=[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"]
;
без необходимости делать это:
self.myName=[[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"] retain];
... и не нужно беспокоиться, если строковое значение внезапно исчезнет, когда произойдет слив автозапуска.
Однако, если вы когда-нибудь позвоните...
[self.myName release];
...где-нибудь за пределамиdealloc
, тогда объект в собственности может быть обнулен, если вы не отслеживаете его постоянно. К тому же, если вы позвоните..
[self.myName retain];
... в любом месте, объект в свойстве будет вытекать (возможно, даже после освобождения объекта self).
Вот почему я говорю никогда не сохранять или автоматически освобождать какой-либо объект, назначенный или скопированный в свойстве. Это не только бессмысленно, но и непродуктивно. Из этого следует, что вы хотите вызывать release для свойства только тогда, когда вы закончили с объектом self, потому что эффективное отслеживание счетчика сохранений установщиком означает, что вы можете обнулить свойство, даже если оно вам все еще необходимо.
Авторелиз никогда не требуется для свойства, потому что свойство всегда сохраняется объектом self, и любой другой объект должен обрабатывать сохранение внутренне, если он использует свойство объекта self.
Как только вы поймете, что происходит внутри сгенерированных средств доступа, правила станут очевидными. Никогда не сохраняйте объект свойства явно. Никогда не выпускайте объект свойства, кроме как в dealloc. Никогда не высвобождайте объект автоматически.
Очевидное следствие этих правил - всегда использовать ссылки self.propertyName внутри объекта self, чтобы обеспечить автоматическое управление сохранением свойства.