Re: Correct use of NSViewController
Re: Correct use of NSViewController
- Subject: Re: Correct use of NSViewController
- From: Cathy Shive <email@hidden>
- Date: Fri, 21 Mar 2008 22:31:56 +0100
Yeah, this is something I ran into dealing with the fact that I have
several key value observations set up. The way I deal with it is to
give my view controller's abstract superclass a -(void)
removeObservations method. I set it up to work similarly to how you
use the -(void)dealloc method. A subclass removes all observations
that it is registered for and bindings and call's super's
implementation. The super call makes sure all the subcontrollers get
the message as well.
I call this method on the first level controller in from the window
controller in NSWindow's delegate method, - (void)windowWillClose.
It's the only place I can think to have this happen at the right
time. It has to be before the window controller gets released by the
document and dealloced.
when the window controller's dealloc is called, it just releases its
view controller. the view controller superclass releases its mutable
array of subcontrollers.
don't retain the window controller in the view controller class.
This should just be a weak reference. I think you're doing that right.
so for me:
1, window is about to close (wish there was a more reliable place to
do this, but it has to be before dealloc):
windowController:
- (void)windowWillClose
{
[mViewController removeObservations]; // this causes the method to
be called all the way down the view controller tree
}
2. view controller removes it's observations
viewController:
- (void)removeObservations
{
[wArrayController removeObserver:self forKeyPath:@"selectedObjects"];
[super removeObservations];
}
3. The document closes and the window controller is released and
dealloced:
window controller:
- (void)dealloc
{
[mViewController release];
[super dealloc];
}
3. The first view controller is released and dealloced:
viewController:
- (void)dealloc
{
[mSubcontrollers release];
[super dealloc];
}
Everything gets released and dealloced and all observations were
removed before deallocs were called.
Now, I'm not sure about how to deal with bindings that you set up in
Interface Builder in terms of controlling *when* the bindings are
removed and the role of the "representedObject" in all of this.
Would have to do a little bit of research on that.
On Mar 21, 2008, at 9:49 PM, Jonathan Dann wrote:
Hi Cathy and Paul,
Thanks to you both for your help, I'm really starting to get
somewhere with this now.
I now have a view controller hierarchy that reflects the views in
my app. I've defined my own view controller subclass that I use
and an abstract superclass for the view controllers I use in the
app. To this 'ESViewController' class I've added a, parent ivar, a
mutable children array and a few indexed accessor methods for that.
To add a new view controller to the hierarchy I use -addChild:
(ESViewController *)vC which has the side-effect of setting the
parent and (originally) the window controller of the child. The
parent will be the view controller that calls -addChild: and the
window controller of the 'root' view controller of the tree is set
when I first make the root view controller.
Cathy, your suggestion of adding the window controller to the views
and their children so I could get the document seemed to work at
first, but I kept getting a warning when closing my document. One
of my children view controllers had an NSObjectController with the
content binding set to @"file's owner.windowController.document",
which worked fine until I tried to close the document. I was told
that the window controller was being deallocated while key-value
observers were still registered with it, which I assume was the
NSObjectController further down in the view hierarchy.
I think this has something to do with retains, as the window
controller was not retained by the view controllers, I couldn't get
my head around who should retain who as my -dealloc methods look
like this:
// ESViewController (inherits from NSViewController and used as
abstract superclass for all my view controllers)
- (void)dealloc;
{
parent = nil; // non-retained ivar
self.children = nil;
windowController = nil;
[super dealloc];
}
// ESWindowController (my window controller for my document)
- (void)dealloc
{
self.rootViewController = nil; // rootViewController is an
instance of ESSplitViewController which places a split view in the
window's content view)
[super dealloc];
}
My windowController's -awakeFromNib method set the root view
controller, which when instantiated created its children, setting
their window controllers and parents as such
// ESWindowController
- (void)awakeFromNib;
{
ESSplitViewController *root = [[ESSplitViewController alloc]
initWithNibName:@"SplitView" bundle:nil];
[root setWindowController:self];
[self setRootViewController:root];
[root release];
root = nil;
}
// ESSplitViewController
- (id)initWith..........
{
if (![super init])
return nil;
ESOutlineViewController *oVC = [[ESOutlineViewController alloc]
initWithNibName:@"OutlineView" bundle:nil]; // the OutlineView nib
has the NSObjectController that causes the deallocation grief
ESTextViewController *tVC = [[ESTextViewController alloc]
initWithNibName:@"TextView" bundle:nil];
[self addChild:oVC];
[self addChild:tVC];
[oVC release];
[tVC release];
return self;
}
- (void)addChild:(ESViewController *)child;
{
[child addObject:child];
[child setWindowController:self.windowController];
[child setParent:self];
}
So the way I expected it to work, when deallocating was as follows
windowController gets a -dealloc call
rootViewController gets released and -dealloc'd
childrenViewControllers get released and -dealloc'd
at the end of all this the control returns to the
windowController's -dealloc method which proceeds to call [super
dealloc];
Some amount of logging later showed me that my windowController was
calling super before the children began their dealloc methods,
which leads me to assume that maybe the unbinding of the
NSObjectController couldn't happen as the windowController to which
it was bound was already gone.
So this boils down to a couple of ideas
Why could the window controller complete it's deallocation before
the children view controllers have, when they are definitely not
retained elsewhere?
Should the view controllers have retained the window controller and/
or their parent (instinct leads me to think this would cause retain
cycles as the window controller will not call -dealloc as it is
retained by view controllers which it needs to release first).
I later changed this code with Paul's suggestion of using the
represented object as a pointer to my document subclass and all
this went away. The -addChild method now sets the represented
object of the child to that of its parent instead of the window
controller. Am I right in thinking that because of the document
architecture the document cannot be deallocated until the window
controller is, so deallocating the window controller removes the
binding before the document receives -dealloc. In that case, if I
did wish to bind something in my view controllers to the window
controller, how can I avoid all this, is there something more
sneaky going on in setting the representedObject other than just
retaining my document? Incidentally the window controller still
calls [super dealloc] before the children of the root view
controller have been deallocated.
Thanks so much to you both for your time, this has really allowed
to to break up my code nicely, an taught me a lot.
Jonathan
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden