0

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

WindowController设置为@property (retain)'d of MainController

-deallocMainController,我愿意self.windowController = nil;

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

为什么会这样?


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


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

主驱动:

[[MainController new] release];

主控制器.h:

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

@property (retain) WindowControllerSubclass *wc;

@end

主控制器.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
4

3 回答 3

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. 

这就是你所看到的。

- - 更新 - -

更准确地说,autorelease pool 的神秘用法,源于你的一行

[self.wc release]; 

这使用 的吸气剂wc,即它调用[self wc]. 现在,在obj-c 运行时的这一部分中实现了默认的合成 getter ,特别是objc_getProperty_non_gc. 请注意,您的属性是(retain),即它是(atmomic retain)。为了保证原子性,getterretain是 ivar,然后在autorelease'ing 之后返回它:

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];
于 2011-08-15T03:00:03.603 回答
0

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

于 2011-08-15T02:47:18.390 回答
0

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

更新:

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

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

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

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

于 2011-08-15T03:24:54.040 回答