Почему это создает утечку памяти (iPhone)? - PullRequest
8 голосов
/ 05 марта 2009
//creates memory leak
  self.editMyObject = [[MyObject alloc] init];

//does not create memory leak
  MyObject *temp = [[MyObject alloc] init];
  self.editMyObject = temp;
  [temp release];

Первая строка кода создает утечку памяти, даже если вы делаете [self.editMyObject release] в методе dealloc класса. self.editMyObject имеет тип MyObject. Вторая строка не несет утечки памяти. Первая строка неверна или есть способ освободить память?

Ответы [ 6 ]

10 голосов
/ 05 марта 2009

Правильное поведение зависит от объявления editMyObject @property. Предполагая, что это delcared как

@property (retain) id editMyObject; //id may be replaced by a more specific type

или

@property (copy) id editMyObject;

затем назначение через self.editMyObject = сохраняет или копирует назначенный объект. Так как [[MyObject alloc] init] возвращает сохраненный объект, которым вы, как вызывающий объект, владеете, у вас есть дополнительное сохранение экземпляра MyObject, и поэтому он утечет, если не будет соответствующего выпуска (как во втором блоке). Я бы посоветовал вам прочитать Руководство по программированию управления памятью [2].

Ваш второй кодовый блок правильный, при условии, что свойство объявлено, как описано выше.

p.s. Вы не должны использовать [self.editMyObject release] в методе -dealloc. Вам следует позвонить [editMyObject release] (при условии, что поддержка @property для ivar называется editMyObject). Вызов метода доступа (через self.editMyObject безопасен для @synthesized методов доступа, но если переопределенный метод доступа полагается на состояние объекта (которое может быть недопустимым в месте вызова в -dealloc или вызывает другие побочные эффекты, у вас вызывающий метод доступа.

[2] Правила владения объектами в Какао очень просты: если вы вызываете метод с alloc или copy в своей подписи (или используете +[NSObject new], который в основном эквивалентен [[NSObject alloc] init]), то Вы «владеете» возвращаемым объектом, и вы должны сбалансировать приобретение прав собственности с release. Во всех остальных случаях вы не являетесь владельцем объекта, возвращаемого методом. Если вы хотите сохранить его, вы должны вступить во владение с retain, а затем отказаться от владения с release.

8 голосов
/ 05 марта 2009

Ваша собственность объявлена ​​как «сохраненная», что означает, что переданный объект автоматически сохраняется.

Поскольку у вашего объекта уже был счетчик ссылок, равный единице в alloc / init, то есть две ссылки, и я предполагаю, что только один релиз (в вашем деструкторе).

По сути, вызов self.editMyObject действительно делает это;

-(void) setEditMyObject:(MyObject*)obj
{
  if (editMyObject)
  {
    [editMyObject release];
    editMyObject = nil;
  }

  editMyObject = [obj retain];
}
4 голосов
/ 05 марта 2009

По соглашению в Какао и касании Какао любой объект, созданный с использованием [[SomeClass alloc] initX] или [SomeClass newX], создается с счетом сохранения один. Вы несете ответственность за вызов [someClassInstance release], когда закончите работу с новым экземпляром, как правило, с помощью метода dealloc.

Ситуация становится сложной, когда вы назначаете новый объект свойству вместо переменной экземпляра. Большинство свойств определены как retain или copy, что означает, что они либо увеличивают счетчик сохранения объекта при его установке, либо создают копию объекта, оставляя оригинал нетронутым.

В вашем примере это, вероятно, есть в вашем файле .h:

@property (retain) MyObject *editMyObject;

Итак, в вашем первом примере:

// (2) property setter increments retain count to 2
self.editMyObject = 

    // (1) new object created with retain count of 1
    [[MyObject alloc] init];

// oops! retain count is now 2

Когда вы создаете свой новый экземпляр MyObject, используя alloc / init, он имеет счет сохранения один. Когда вы назначаете новый экземпляр для self.editMyObject, вы фактически вызываете метод -setEditMyObject:, который компилятор создает для вас, когда вы @synthesize editMyObject. Когда компилятор видит self.editMyObject = x, он заменяет его на [self setEditMyObject: x].

Во втором примере:

MyObject *temp = [[MyObject alloc] init];
// (1) new object created with retain count of 1

self.editMyObject = temp;
// (2) equivalent to [self setEditMyObject: temp];
// increments retain count to 2

[temp release];
// (3) decrements retain count to 1

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

См. Также Стратегия какао для управления указателем / памятью

3 голосов
/ 05 марта 2009

Первая версия создает объект без соответствующего выпуска. Когда вы выделяете объект, это означает, что вы являетесь владельцем этого объекта. Предполагается, что ваш сеттер сохраняет объект (как и должен), то есть теперь вы владеете объектом дважды. Релиз нужен для балансировки создания объекта.

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

1 голос
/ 05 марта 2009

Все остальные уже рассказали, почему это вызывает утечку памяти, поэтому я просто расскажу, как избежать переменной 'temp' и все же предотвратить утечку памяти:

self.editMyObject = [[[MyObject alloc] init] autorelease];

Это оставит вашу (сохраненную) собственность в качестве единственного владельца нового объекта. Точно такой же результат, как во втором примере, но без временного объекта.

0 голосов
/ 23 октября 2009

Было решено и объяснено, что код ниже не имеет утечки (при условии @property retain и @synthesize для editMyObject):

//does not create memory leak
MyObject *temp = [[MyObject alloc] init];
self.editMyObject = tempt;
[temp release];

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

//does not create memory leak ?
self.editMyObject = [[MyObject alloc] init];
[editMyObject release];

Для меня это выглядит нормально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...