Non-modal windows memory management
I'm a newbie in Mac Dev. I come from iPhone dev. My question relates to non-modal windows management. It's quite different from the iPhone and it's memory management model.
Say for example, i have a preference window, i can use something like that to show the window:
-(IBAction)showPreferenceController:(id)sender {
if (!preferenceController) {
preferenceController = [[PreferenceController alloc]init];
}
[preferenceController showWindow:preferenceController];
}
But with this code, the window will stay in memory during app life because the window is never released.
To avoid that, I could also use the method described here :
stackoverflow.com/questions/1391260/who-owns-an-nswindowcontroller-in-standard-practice Create inPreferenceController
a + (id) sharedInstance
and release the window using (void)windowWillClose:(NSNotific开发者_Python百科ation *)notification
I see many cocoa code samples where non-modal windows are never released.
For example here : http://www.mattballdesign.com/blog/2008/10/01/building-a-preferences-window/ : The preference panel and all the subviews are created in awakeFromNib
and so will live in memory during all app life.
If you take for example Xcode app, there are a lot of non-modal windows :
- Global Find window (CMD+MAJ+F) - App Info Panel - Help Window - ...I suppose that these windows are released when they are closed to keep memory as low as possible. I would like some advices to know the best way to manage non-modal windows in a cocoa app. Keep in memory? Releasing asap? I know a mac has a lot of memory compared to an iPhone but I also think it's not good to keep in memory objects we are not using.
Thanks.
Edited with Rob post :
I send -autorelease to the window and set my pointer to nil so I'll recreate the window later. This is similar to the technique you cite, though whether or not to use +sharedController for the Controller isn't related; you can do this whether you have a shared controller or not.
I don't how to do that without a singleton (+sharedController).
I explain what I mean with this example: In the app Controller :@interface AppController : NSObject <NSApplicationDelegate> {
Implementation :
-(IBAction)showPreferenceController:(id)sender {
if (!preferenceController) {
preferenceController = [[PreferenceController alloc]init];
}
[preferenceController showWindow:preferenceController];
}
In the preferences controller :
@interface PreferenceController : NSWindowController <NSWindowDelegate>
Implementation :
- (void)windowWillClose:(NSNotification *)notification {
[self autorelease];self=nil;
}
It will crash when i close and reopen after the window : preferenceController is released but not equal to nil. So I need to set preferenceController to nil when the window is closed. There is no problem to do that with a singleton.
Without singleton, I should set appController as the delegate of Preference Window to be able to set preferenceController to nil when the window is closed. But i don't like that way.Edited with Preston comments
I didn't say it but I want only one instance of my non-modal window even if we call-(IBAction)showPreferenceController:(id)sender
several times.
That's why I test if preferenceController is equal to nil or not in appController.
So, I need to set preferenceController to nil in appController if we close the window.
So the solution would be :
In AppController, listening NSWindowWillCloseNotification:
- (void)windowWillClose:(NSNotification *)notification {
if ([notification object] == [preferenceController window]) {
[preferenceController autorelease];
preferenceController = nil;
}
}
Is it correct? Is this the only one solution? because it seems a little bit complicated just to manage my non modal window...
You're thinking in all the right ways here. It's not ok to leak memory just because you have a lot of it. That said, it is common not to release windows just because they close on Mac. You should generally hold onto them if you think they're going to be opened again to avoid the cost of reloading them.
There are two schools of thought on NSWindow
: owned and unowned. I am of the "owned" school of thought. I generally give every window an owner that retains it with an ivar and releases it when appropriate. Generally that is the delegate, sometimes it's the app controller. In -windowShouldClose:
, I send -autorelease
to the window and set my pointer to nil so I'll recreate the window later. This is similar to the technique you cite, though whether or not to use +sharedController
for the Controller isn't related; you can do this whether you have a shared controller or not.
The unowned school of thought is to use NSWindow's -setReleasedWhenClosed:
so that it automatically releases itself when it closes. I believe several of the windows you reference do it this way when they are provided by the system, since there might not be a delegate. This can be useful in certain kinds of panels, but I would be careful with it as a general pattern. It's better to have explicit memory management in almost all cases.
If you check "Release When Closed" for your window in Interface Builder or set it programmatically, it will release itself. You can also have the window's delegate autorelease the window in the windowShouldClose: delegate method.
If you want to release the window controller when the window closes, have your window controller listen for its window's NSWindowWillCloseNotification and do a [self autorelease] in that method. I'm not a fan of creating a singleton preferences controller always sticking around that the user will, in practice, rarely interact with.
精彩评论