iPhone: это утечка или нет - PullRequest
       10

iPhone: это утечка или нет

5 голосов
/ 12 февраля 2010

Недавно кто-то в 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 ]

7 голосов
/ 12 февраля 2010

Подсчет сохранений и выпусков помогает в этом случае. Это определенно утечка. Ваш объект locationManager будет сохранен 2 раза: один раз при вызовах alloc/init и один раз у свойства. Установка свойства nil приведет к сбросу locationManager только один раз.

Для примеров, приведенных в Редактировании 1, они действительно одинаковы. Похоже, что другой разработчик не хочет немедленного автоматического выпуска или не совсем понимает, что делает autorelease.

4 голосов
/ 12 февраля 2010

Семантика опции свойства retain :

  • старое значение (если есть) получает сообщение об освобождении
  • новое значение получает сообщение об удержании

Следовательно, ваш экземпляр CLLocationManager будет иметь счет сохранения 2 после установщика. Один из alloc и один из сохраняющего сеттера. Вы должны отправить сообщение release сразу после установщика:

CLLocationMamnager *aLocationManager = [[CLLocationManager alloc] init];
self.locationManager = aLocationManager;
[aLocationManager release];

В качестве альтернативы, добавьте его в пул авто-релиза, чтобы он, по крайней мере, был в конечном итоге бесплатным. Как ты сам написал:

self.locationManager = [[[CLLocationManager alloc] init] autorelease];

Еще лучше, не используйте параметр свойства retain . Оставьте это как назначить (по умолчанию), и вы готовы к работе, так как вы все равно используете сохраненный объект.

2 голосов
/ 12 февраля 2010

Следующее утверждение сохраняется дважды, и как таковое должно быть выпущено дважды:

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;
1 голос
/ 13 февраля 2010

Простое практическое правило. Если свойство помечено как «сохранить», всегда присваивайте ему автоматически выдаваемую переменную (если вы ее создаете), если, конечно, вам также НЕ НУЖНО сохранить его в другом месте.

1 голос
/ 12 февраля 2010

@ jnic: вы удивительно неправы. Насколько я понимаю, предыдущему значению свойства отправляется сообщение о выпуске, а не объект, который вы хотите присвоить свойству. так что, да, предлагаемый код действительно пропадает, и вы должны отправить сообщение об автоматическом выпуске, как вы и думали, Ахмет Эмра

0 голосов
/ 12 февраля 2010

Хорошо, вот сделка.

Когда вы определяете свойство как ...

@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];

... где угодно, тогда объект в свойстве будет вытекать (возможно, даже после того, как собственный объект был освобожден.)

Вот почему я говорю, что никогда не следует сохранять или автоматически освобождать любой объект, назначенный или скопированный в свойстве. Это не только бессмысленно, но и контрпродуктивно. Из этого следует, что вы хотите вызывать release для свойства только тогда, когда вы закончили с объектом self, потому что эффективное отслеживание счетчика сохранений установщиком означает, что вы можете обнулить свойство, даже если оно вам все еще необходимо.

Авторелиз никогда не требуется для свойства, потому что свойство всегда сохраняется объектом self, и любой другой объект должен обрабатывать сохранение внутренне, если он использует свойство объекта self.

Как только вы понимаете, что происходит внутри сгенерированных средств доступа, правила становятся очевидными. Никогда не сохраняйте объект свойства явно. Никогда не выпускайте объект свойства, кроме как в dealloc. Никогда не высвобождайте объект автоматически.

Очевидным следствием этих правил всегда является использование ссылок self.propertyName внутри объекта self для обеспечения автоматического управления сохранением свойства.

0 голосов
/ 12 февраля 2010

Добавьте эту строку внизу ... Это, очевидно, дает ваш счет сохранения для locationManager. Это скажет вам, если вам нужно выпустить его вручную или нет.

NSLog(@"retainCount:%d", [locationManager retainCount]);

редактировать:

[locationManager release];
...