我在下面的超级简单代码中遇到了完全出乎意料的时序问题。其中一个变量是自动释放,我不知道为什么。我不使用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
答案 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;
x
和y
可以有所不同,如果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
将会或不会被调用。您甚至不能假设在运行循环结束时池将立即耗尽,因为可能存在自定义运行循环。
修复您的对象,以便它们可以处理任何可能的释放序列。