@property(保留)自动释放或释放对象吗?

时间:2011-08-15 02:42:14

标签: objective-c cocoa xcode4 nsautoreleasepool nswindowcontroller

我在下面的超级简单代码中遇到了完全出乎意料的时序问题。其中一个变量是自动释放,我不知道为什么。我不使用autorelease,KVO等。它不应该发生。

WindowController被设置为@property (retain)的{​​{1}}'d。

MainController的{​​{1}}中,我-dealloc

但是,它一直等到自动释放池被刷新以释放windowController。我希望MainController完成后立即调用WindowController的dealloc。即使我在NSAutoreleasePool中包装[mainController release],它仍然不会立即释放。

为什么会这样?


对于@property / NSWindowController,这似乎不是正确的行为。我错过了什么吗?


更正:绑定。我正式不知道问题是什么。

主要驱动因素:

self.windowController = nil;

MainController.h:

self.windowController = nil

MainController.m:

[[MainController new] release];

MainWindowControllerSubclass.h:

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

@property (retain) WindowControllerSubclass *wc;

@end

MainWindowControllerSubclass.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

3 个答案:

答案 0 :(得分:3)

它没有什么奇怪的,特别是如果你的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的getter,即调用[self wc]。现在,默认的合成getter在this portion of obj-c runtime中实现,特别是objc_getProperty_non_gc。请注意,您的媒体资源为(retain),即(atmomic retain)。为了保证原子性,吸气剂retain是ivar,然后在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;

xy可以有所不同,如果obj执行了一些聪明的缓存,或者是否有另一个访问obj的线程在两者之间发生了更改obj.property线。因此,请改用临时变量:

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

答案 1 :(得分:0)

尝试用self.windowController = nil;替换[self.windowController release];(前一种类型的代码,即将事物设置为nil,(有时)在垃圾收集环境中使用,但不是在dealloc方法中。)

答案 2 :(得分:0)

您的期望不对。这是引用计数内存管理的关键思想:release永远不能保证解除分配;当保留它的每个人决定释放时,该对象才被释放。如果它仍然保留在某个地方,你就无法强行解除分配。

更新:

首先,属性getter实现为return [[wc retain] autorelease](参见Yuji的回答)。

其次,Cocoa也可以保留并自动释放你的WindowController。如果它现在没有发生,那并不意味着当你添加更多代码时它不会发生。

您不应该对解除分配的顺序做出假设,或者autorelease将会或不会被调用。您甚至不能假设在运行循环结束时池将立即耗尽,因为可能存在自定义运行循环。

修复您的对象,以便它们可以处理任何可能的释放序列。