I’m having completely unexpected timing problems with the super simple code below. One of the variables is being autoreleased, and I have no clue why. I’m not using autorelease, KVO, etc. It shouldn’t be happening.
The WindowController is set as an @property (retain)‘d of MainController.
In the -dealloc of the MainController, I do self.windowController = nil;
But, it keeps waiting until the autorelease pool is flushed to release the windowController. I expect the WindowController’s dealloc to be called as soon as self.windowController = nil is done. Even if I wrap the [mainController release] in NSAutoreleasePool, it still doesn’t release right away.
Why is this happening?
This doesn’t seem like proper behavior for the @property / NSWindowController. Am I missing something?
Correction: It’s not bindings. I officially have no clue what the problem is.
Main driver:
[[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
There’s nothing strange about it, especially if your
NSWindowControlleris in an autorelease pool.An object (say
x) is dealloc’ed when every object which owns it releases it.Autoreleaseis a deferred release, i.e. it doesn’t actually release until the autorelease pool is drained.Consider the following chain of events:
That’s what you’re seeing.
— Update —
More, precisely, the mysterious usage of autorelease pool arises from your line
This uses the getter of
wc, i.e. it calls[self wc]. Now, the default synthesized getter is implemented in this portion of obj-c runtime, in particularobjc_getProperty_non_gc. Note that your property is(retain), i.e. it’s(atmomic retain). To guarantee atomicity, the getterretains the ivar and then returns it afterautorelease‘ing it:That’s why it’s put on the autorelease pool. In any case,
is a bad idea. In your case,
self.ivarin the second line returned what you assigned in the first line, but that’s not guaranteed in the case of clever, non-synthesized accessors, or in a multi-threaded environment. When you doxandycan be different, ifobjdoes some clever caching, or if there’s another thread accessingobjwhich changesobj.propertybetween the two lines. So, use a temporary variable instead: