Имеет ли @property (удерживать) авто-релиз или освобождение объектов? - PullRequest
0 голосов
/ 15 августа 2011

У меня совершенно неожиданные проблемы с синхронизацией из-за очень простого кода ниже. Одна из переменных автоматически высвобождается, и я понятия не имею, почему. Я не использую autorelease, KVO и т. Д. Этого не должно быть.

WindowController устанавливается как @property (retain) 'd из MainController.

В -dealloc из MainController я делаю self.windowController = nil;

Но он продолжает ждать, пока пул автоматического выпуска не будет сброшен, чтобы освободить оконный контроллер. Я ожидаю, что dealloc WindowController будет вызван, как только self.windowController = nil будет сделано. Даже если я заверну [выпуск mainController] в NSAutoreleasePool, он все равно не будет выпущен сразу.

Почему это происходит?


Это не похоже на правильное поведение для @property / NSWindowController. Я что-то упустил?


Исправление: это не привязок. Я официально понятия не имею, в чем проблема.

Главный драйвер:

[[MainController new] release];

MainController.h:

#import <Foundation/Foundation.h>
#import "WindowControllerSubclass.h"
@interface MainController : NSObject {
    WindowControllerSubclass *wc;
}

@property (retain) WindowControllerSubclass *wc;

@end

MainController.m:

#import "MainController.h"

@implementation MainController

@synthesize wc;

- (id)init {
    if (self = [super init]) {
        // This is problem here >>>  If I assign directly to wc, then it's not added to autorelease pool
        self.wc = [[WindowControllerSubclass alloc] init];
        [self.wc release]; // since it's (retain)'d
    }

    return self;
}

- (void) dealloc {
    self.wc = nil;
    NSLog(@"%@ deallocd (should be called after WC's dealloc)", [self className]);
}

@end

MainWindowControllerSubclass.h:

#import <Cocoa/Cocoa.h>

@interface WindowControllerSubclass : NSObject /* Not even NSWindowController */
@end

MainWindowControllerSubclass.m:

#import "WindowControllerSubclass.h"

@implementation WindowControllerSubclass

- (void) dealloc {
    NSLog(@"%@ deallocd", [self className]);
}

@end

Ответы [ 3 ]

3 голосов
/ 15 августа 2011

В этом нет ничего странного, особенно если ваш NSWindowController находится в пуле авто-релиза.

Объект (скажем, x) освобождается, когда каждый объект, которому он принадлежит, освобождает его.Autorelease является отложенным выпуском, то есть фактически не выпускается до тех пор, пока не будет истощен пул автоматического выпуска.

Рассмотрим следующую цепочку событий:

    B creates x
    A owns x
    A autoreleases x. // x is not released; it's put on an autorelease pool
    B releases x.     // x is not dealloced yet, because x is not released by the autorelease pool
    autorelease pool is drained. x is sent another release message. nobody owns x. x is dealloc'd. 

Вот что вы видите.

--- Обновление ---

Еще,точно, таинственное использование пула автоматического выпуска возникает из вашей строки

[self.wc release]; 

При этом используется геттер wc, то есть он вызывает [self wc].Теперь синтезированный метод получения по умолчанию реализован в этой части времени выполнения obj-c , в частности objc_getProperty_non_gc.Обратите внимание, что ваша собственность (retain), то есть (atmomic retain).Чтобы гарантировать атомарность, получатель retain получает ивар, а затем возвращает его после autorelease его:

id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;

// Atomic retain release world
spin_lock_t *slotlock = &PropertyLocks[GOODHASH(slot)];
_spin_lock(slotlock);
id value = objc_retain(*slot);
_spin_unlock(slotlock);

// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);

Вот почему он включен в пул авто-релиза.В любом случае,

obj.property=[[SomeClass alloc] init];
[obj.property release];

- плохая идея.В вашем случае self.ivar во второй строке вернуло то, что вы назначили в первой строке, но это не гарантируется в случае умных несинтезированных средств доступа или в многопоточной среде.Когда вы делаете

obj.property=x;
id y=obj.property;

x и y, они могут различаться, если obj выполняет какое-то умное кэширование, или если есть другой поток, обращающийся к obj, который меняет obj.property между двумя строками,Поэтому вместо этого используйте временную переменную:

SomeClass* a=[[SomeClass alloc] init];
obj.property=a;
[a release];
0 голосов
/ 15 августа 2011

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

ОБНОВЛЕНИЕ:

Во-первых, метод получения свойства реализован как return [[wc retain] autorelease] (см. Ответ Юджи).

Во-вторых, Какао может сохранить и автоматически выпустить ваш WindowController.Если это не происходит сейчас, это не значит, что этого не произойдет, когда вы добавите больше кода.

Вы НЕ ДОЛЖНЫ делать предположения о порядке освобождения, или что autorelease будет или не будет вызываться.Вы даже не можете предположить, что пул будет очищен сразу в конце цикла выполнения, так как может выполняться пользовательский цикл выполнения.

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

0 голосов
/ 15 августа 2011

Попробуйте заменить self.windowController = nil; на [self.windowController release]; (первый тип кода, то есть установка nil, (иногда) используется в среде сборки мусора, но не в методе dealloc.)

...