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

Недавно кто-то на Переполнении стека сказал мне, что код ниже не просачивается, что свойство обрабатывает само хранение:

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

мудрый управлением памятью?

Парень говорит, что первый корректен и отказывается от второго. Почему второй был бы НАСТОЛЬКО НЕПРАВИЛЬНЫМ? Насколько я вижу, что оба присваивают автовыпущенные экземпляры некоторым свойствам, но так или иначе существует все еще упрямый аргумент, что второй является неправильным. Я не вижу его, любая справка так приветствовалась бы.

5
задан ahmet emrah 12 February 2010 в 19:09
поделиться

7 ответов

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

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

7
ответ дан 18 December 2019 в 14:45
поделиться

Семантика параметра свойства сохранить такова:

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

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

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

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

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

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

4
ответ дан 18 December 2019 в 14:45
поделиться

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

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

Возможно, лучше всего написать так:

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

Теперь эта переменная сохраняется только один раз, и вы можете освободить ее в функции dealloc вашего класса.

Также, если вы выполните следующую строку кода, будет вызвано освобождение:

locationManager = nil;

Поскольку locationManager синтезирован, при установке его в nil он освобождается первым.

Более того, если вы сделаете следующее, locationManager будет сначала освобожден, а затем сброшен за кулисами:

self.locationManager = foo;

Наконец, следующий код завершится с ошибкой exc_bad_access, потому что вы дважды освобождаете locationManager, когда устанавливаете его в foo:

self.locationManager = [[[CLLocationManager alloc] init]autorelease];
[locationManager release];
self.locationManager = foo;
2
ответ дан 18 December 2019 в 14:45
поделиться

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

1
ответ дан 18 December 2019 в 14:45
поделиться

C, 99 знаков

Не победитель, но, возможно, есть место для улучшения. Никогда раньше этого не делал. Оригинальная концепция, первый черновик.

#define l w|=*b&b[s]&b[2*s];b+=3/s;s
f(int*b){int s=4,w=0;l=3;l;l;l=2;--b;l=1;b-=3;l;l;return l;}

Спасибо KennyTM за несколько идей и тест-жгут.

"Версия разработки":

#define l w|=*b&b[s]&b[2*s];b+=3/s;s // check one possible win
f( int *b ) {
        int s=4,w=0; // s = stride, w = winner
        l=3;     // check stride 4 and set to 3
        l;l;l=2; // check stride 3, set to 2
        --b;l=1; // check stride 2, set to 1
        b-=3;l;l; return l; // check stride 1
}
-121--1152344-

Типичные настройки скрипты позволяют много запускать небольшие подпроцессы. В Unix-подобных операционных системах это делается с помощью вызовов функций fork () и exec () , которые имеют очень особую семантику, которую необходимо сохранить (например, копирование на запись общей памяти после форкинга). В Windows подпроцессы создаются с помощью CreateProcess () , который имеет разную семантику (например, полностью отделить пространство памяти от родительского). Для правильного выполнения Unix-подобных скриптов и программ MSYS необходимо проделать большую работу по эмуляции, чтобы создать новые процессы в Windows, такие как fork ()/exec () в Unix. Это в конечном итоге происходит медленнее, чем ОС, которая предлагает эти вызовы функций.

-121--1678519-

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

1
ответ дан 18 December 2019 в 14:45
поделиться

Хорошо, вот в чем дело.

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

@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 right.", @ "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, чтобы гарантировать автоматическое управление сохранением свойств.

-2
ответ дан 18 December 2019 в 14:45
поделиться

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

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

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

[locationManager release];
0
ответ дан 18 December 2019 в 14:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: