Управление памятью - как лучше инициализировать экземпляр, объявленный в заголовке - PullRequest
1 голос
/ 06 октября 2010

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

Вы объявляете переменную в своем заголовке, например, так:

@interface SomeClass : NSObject {
    NSMutableArray *anArray;
}

@property (nonatomic, retain) NSMutableArray *anArray;

end

А затем в своем основном файле вы синтезируете его и устанавливаете его в начальное значение:

    @implementation SomeClass

@synthesize anArray

- (SomeClass *)init{
    if (self = [super init]) {
        self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
}
[return self];

И отпустите его, когда ваш класс освободит:

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

Теперь, когда я запускаю инструменты, строка

self.anArray = [[NSMutableArray alloc] initWithCapacity:10];

определяется как утечка памяти.Это утечка памяти, потому что когда вы определяете переменную anArray в заголовке, она выделяет память?(Потому что я думал, что это нулевой указатель.) Поэтому, когда вы хотите инициализировать его и вызываете [[NSMutableArray alloc] initWithCapacity: 10], вы перераспределяете память и теряете указатель на исходное распределение?

Поэтому вместо этого я использую метод класса удобства:

@implementation SomeClass

    @synthesize anArray

    - (SomeClass *)init{
        if (self = [super init]) {
            self.anArray = [NSMutableArray arrayWithCapacity:10];
    }
    [return self];

Это больше не идентифицируется как утечка памяти в инструментах.А поскольку это удобный метод, anArray автоматически освобождается.Тем не менее, если я предполагаю, что объявление экземпляра в заголовке выделяет память, что объясняет предыдущую проблему, то должен ли я по-прежнему выпускать массив?Сохраняет ли установка начальных значений таким образом, возможно, это?

Я понимаю разницу между

NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:10];

и

NSMutableArray *anArray = [NSMutableArray arrayWithCapactiy:10];

, но я не уверен, чтоВы понимаете, когда вы объявили NSMutableArray * anArray в своем заголовке, какой из двух подходов вы должны использовать и почему.И независимо от того, используете ли вы второй подход или нет, вы все равно должны освобождать anArray при вызове dealloc.

Могу добавить, что я нашел полезными следующие посты / ссылки:

Ответы [ 2 ]

3 голосов
/ 06 октября 2010

выделение объекта запускает его со счетчиком ссылок 1. Установка свойства, имеющего атрибут 'retain', также увеличивает количество ссылок.

Значит, это обычно плохо:

@property (nonatomic, retain) Object * variable;

...

self.variable = [[Object alloc] init];

Поскольку переменная теперь имеет счетчик ссылок 2.

При установке переменной-члена объекта, просто сделайте это:

variable = [[Object alloc] init];

Вы также должны понимать, что это работает

        self.anArray = [NSMutableArray arrayWithCapacity:10];

Поскольку «arrayWithCapacity» (и другие аналогичные факторные методы) автоматически высвобождает объект, который он возвращает, поэтому после установки свойства он по существу имеет счетчик ссылок 1.

2 голосов
/ 06 октября 2010

Это не тот экземпляр, который выделяет память. Вы правы, если предположите, что в Objective-C (по крайней мере, во всех операционных системах на базе Apple) для вновь инициализированных классов все их ивары имеют значение 0 (или ноль или NULL в зависимости от ситуации).

Проблема, с которой вы сталкиваетесь, заключается в том, что вы используете свойство, а не ивар при инициализации. Поскольку вы объявили свое свойство как retain , то при использовании средства доступа к свойству свойства оно автоматически сохраняется.

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

Так что просто помните, что если вы когда-либо будете использовать средства доступа к свойству, даже внутри самого класса, вы получите функции, которые вы установили для свойства (например, неатомарные, сохраняемые и т. Д.). Вы используете средства доступа к собственности всякий раз, когда выполняете одно из следующих действий:

// in these cases the property takes ownership through the
// retain keyword, so you must not take ownership yourself
self.anArray = something;
[self setAnArray:something];
[self setValue:something forKey:@"anArray"];

Вы получите доступ к своему ивару напрямую, как:

anArray = something; // in this case you must take ownership
...