Двойное освобождение, когда этого не должно происходить - PullRequest
3 голосов
/ 30 июня 2009

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

@protocol SomeDelegate <NSObject>
@required
- (id)initWithCols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
@end


@interface SomeObject : NSObject <SomeDelegate> {
}
- (id)initWithCols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
@end


@interface Layout : UIView {
  id<SomeDelegate> someDelegate;
}
@property(retain) id<SomeDelegate> someDelegate;
- (id)initWithFrame:(CGRect)aRect Cols:(NSUInteger)Cols Rows:(NSUInteger)Rows;
@end


@implementation Layout
@synthesize someDelegate;
- (id)initWithFrame:(CGRect)aRect Cols:(NSUInteger)Cols Rows:(NSUInteger)Rows {

  if(self = [super initWithFrame:aRect]) {
    cols = Cols; 
    rows = Rows;
    id<SomeDelegate> delegate = [[SomeObject alloc] initWithCols:cols Rows:rows];
    [self setSomeDelegate:delegate];
    //[delegate release];
  }
  return self;
}

-(void)dealloc {
    [someDelegate release];
    [super dealloc];
}

@end

Теперь, когда я раскомментирую "// [делегат релиз];" строка в конструкторе класса Layout, затем я получаю ошибку «EXC_BAD_ACCESS» и приложение вылетает при попытке его освобождения. Я проследил падение до освобождения объекта someDelegate в методе dealloc класса Layout. Если я оставлю это комментарий, то приложение работает нормально.

Может кто-нибудь объяснить, почему это происходит, потому что это противоречит всему, что я читал об управлении памятью в Objective-C.

Просто обратите внимание, что пример кода действительно работает, однако мой код не соответствует примеру. Может ли быть что-то внутри моего настоящего SomeObject, что вызывает авто-релиз?

Заранее спасибо.

Ответы [ 3 ]

3 голосов
/ 30 июня 2009

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

Затем включите NSZombieEnabled (в ваших настройках исполняемого файла, на панели «Аргументы» добавьте переменную окружения NSZombieEnabled, для которой установлено значение YES).

Затем добавьте метод dealloc в ваш delagate, если у него его еще нет (обязательно вызовите [super dealloc]!), И поставьте точку останова - он сообщит вам, когда ваш delagate будет освобожден, и сообщит вам когда он будет выпущен.

В качестве альтернативы, добавьте тривиальные методы release / autorelease к вашему классу делегата, которые не делают ничего, кроме вызова, и затем остановите их, и это точно сообщит вам, когда он будет освобожден.

Три заключительных комментария: в стандартном соглашении об именах для Objective C / Cocoa вы должны иметь поля параметров в нижнем регистре, т.е. это должно быть:

- (id)initWithFrame:(CGRect)aRect cols:(NSUInteger)Cols rows:(NSUInteger)Rows;

Когда ваш ivar и свойство имеют одинаковые имена, очень легко случайно использовать неправильное, поэтому я рекомендую использовать другое имя и имя ivar, чтобы избежать путаницы, либо используйте префикс _, например Apple, либо другой префикс. чтобы избежать путаницы с Apple:

  id<SomeDelegate> _someDelegate;

@synthesize someDelegate = _someDelegate;

И Apple рекомендует не использовать сеттеры / геттеры в init / dealloc, поэтому ваш код инициализации должен быть:

_someDelegate = [[SomeObject alloc] initWithCols:cols Rows:rows];
2 голосов
/ 30 июня 2009

Как отмечается в комментариях, проблема, по-видимому, отсутствует в опубликованном коде.

Я мог бы попросить дополнительную информацию, но я твердо в состоянии научить человека ловить рыбу ...

Авария в -релизе часто вводит в заблуждение, так как различные оптимизации, в основном оптимизация хвостовых вызовов, будут выглядеть так, как будто авария произошла на один или два кадра выше фактического вызова, который произошел сбой. Когда происходит сбой, в стеке недостаточно информации, чтобы действительно идентифицировать виновника.

Всякий раз, когда вы подозреваете, что у вас есть какой-либо сбой в -release или -dealloc, немедленно включите зомби. Это можно сделать с помощью Instruments или с помощью переменной окружения или путем вызова функции в Foundation на очень раннем этапе выполнения вашей программы.

Ищите «Zombies» или «NSZombie» в документации, прилагаемой к среде разработки (это было бы скорее «научить человека ловить рыбу»).

0 голосов
/ 02 июля 2009

Проблема заключалась в том, что MutableArray глубоко в подклассе был создан с помощью фабрики (авто-релиз), но я тоже выпускал. К сожалению, сбой не будет указывать, какой унаследованный dealloc вызвал сбой, и просто остановится на первом переопределенном dealloc.

Зомби немного помогло, сказав, что преступником был массив, но не более того. Я думаю, что у NSZombie есть что-то большее, и ему нужно больше опыта, чтобы в полной мере воспользоваться этим.

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