PDA

View Full Version : applicationWillTerminate




netytan
Mar 1, 2006, 01:22 PM
Hey guys,

I'm having a problem with applicationWillTerminate. What I need to do is run something once when the program exits, like applicationWillTerminate is supposed to do but it's not being called. I've set NSApp's delegate in the awakeFromNib of course but no luck.

I tried moving the delegation to other methods just to see what would happen and that triggers applicationWillTerminate, but, also crashes out with an error :mad:.

Will ALL the objects have there dealloc called when the program exits or is it a cold shutdown, giving the memory over to other processes?

If this is the case then this must happen after I/O has been dismantled because NSLog never displays anything in my tests, so confused :(. I don't do a lot with Cocoa because I generally have no need for GUI's so I work more with other languages. Is this a Newbie problem I'm having?

Thanks a lot,

Mark.



caveman_uk
Mar 1, 2006, 01:37 PM
In my apps I generally set the app delegate in the main controllers -init function. I can see why awakeFromNib should be a problem.

You don't generally put your code to release any retained objects in applicationWillTerminate - you put it in the controller class's -dealloc method. I'm not sure from your description if that's what you've done. Whilst I think when an app closes it releases everything even if it's not explicitly released in the code, it's good practise to release anything you've retained. BTW you call 'release' not 'dealloc' on an object to destroy it but in the class itself any tidy up code is in the 'dealloc' method. Odd but true...

Personally I've never needed to have an applicationWillTerminate method as all my tidy-up code is in the -dealloc method of the main app controller.

netytan
Mar 1, 2006, 01:53 PM
In my apps I generally set the app delegate in the main controllers -init function. I can see why awakeFromNib should be a problem.

You don't generally put your code to release any retained objects in applicationWillTerminate - you put it in the controller class's -dealloc method. I'm not sure from your description if that's what you've done. Whilst I think when an app closes it releases everything even if it's not explicitly released in the code, it's good practise to release anything you've retained. BTW you call 'release' not 'dealloc' on an object to destroy it but in the class itself any tidy up code is in the 'dealloc' method. Odd but true...

Personally I've never needed to have an applicationWillTerminate method as all my tidy-up code is in the -dealloc method of the main app controller.

Normally no but I'm embedding an interpreter written in C and I only want it to be shutdown when the application is finished running. Adding it to the dealloc or retain methods in the main document class results in the system getting released whenever a window is closed this would break the interpreter still being used by other windows.

I've tried setting the delegate in -init and the program crash's. This is nothing to do with the interpreter since it happens when I unplug it :(.

Maybe you could explain how you manage to set the delegate in -init while avoiding this? :).

Maybe I could waggle something if objective C has class variables?

Thanks for your reply,

Mark.

robbieduncan
Mar 1, 2006, 02:32 PM
Does NSApp actually awakeFromNib? It's not normally serialised into a nib file so would not wake up. Have you actually checked that the awakeFromNib is called.

On the dealloc point I'm not sure that these are guaranteed to run on app shutdown, but I would have thought that they normally get called.

netytan
Mar 1, 2006, 02:46 PM
Does NSApp actually awakeFromNib? It's not normally serialised into a nib file so would not wake up. Have you actually checked that the awakeFromNib is called.

On the dealloc point I'm not sure that these are guaranteed to run on app shutdown, but I would have thought that they normally get called.

Wow ok, I put a NSLog in awakeFromNib and it's a no go. Where is the delegate usually set. I put it in awakeFromNib because of an example I found on CocoaDevCentral and it crash's in other methods for some reason.

Edit: if it makes a difference this is a document based application.

Mark.

robbieduncan
Mar 1, 2006, 02:55 PM
Personally I do it in Interface Builder!

Open MainMenu.nib (or whatever your main nib is called). Drag the delegate header to the IB window. Create an instance of that object. Now connect the File's Owner to it (and select delegate).

netytan
Mar 1, 2006, 03:33 PM
Personally I do it in Interface Builder!

Open MainMenu.nib (or whatever your main nib is called). Drag the delegate header to the IB window. Create an instance of that object. Now connect the File's Owner to it (and select delegate).

That doesn't quite work for document based applications beacause the files owner is already an instance of that type and has no deligate :(.

You also cant create an instance of NSApplication in order to link that way *sigh*, and it should be so simple.

Mark.

caveman_uk
Mar 1, 2006, 03:37 PM
Does NSApp actually awakeFromNib? It's not normally serialised into a nib file so would not wake up. Have you actually checked that the awakeFromNib is called.

No, but a controller object does which is where I'd put it.

OP, your app controller is an NSObject subclass isn't it? You don't subclass NSApplication - well you can but I've never done it.

BTW, the way I set the NSApp delegate is [NSApp setDelegate:self]; as I usually set it in the init method of the app controller class that is to be the NSApp delegate.

robbieduncan
Mar 1, 2006, 03:38 PM
That doesn't quite work for document based applications beacause the files owner is already an instance of that type and has no deligate :(.

You also cant create an instance of NSApplication in order to link that way *sigh*, and it should be so simple.

Mark.

It does work for document based apps. I do it this way in a document based app. The Files Owner in MainMenu.nib (NOT MyDocument.nib) is an NSApplication.

robbieduncan
Mar 1, 2006, 03:42 PM
OK, I've put a bare example of a Cocoa Document app with a delegate object getting set in Interface Builder here (http://www.robbieduncan.net/AppDelegateExample.tgz).

Updated with applicationWillTerminate (I suppose it's a good idea to demonstrate that it works!)

netytan
Mar 1, 2006, 04:51 PM
OK, I've put a bare example of a Cocoa Document app with a delegate object getting set in Interface Builder here (http://www.robbieduncan.net/AppDelegateExample.tgz).

Updated with applicationWillTerminate (I suppose it's a good idea to demonstrate that it works!)

Are you single because I could kiss you ;). Finally it works. One thing I am noticing though is that the contents of the -init are being run twice, is this because there is now an instance in the MainMenu.nib as-well as the one bound too the main document window?

Man, i'll never doubt you again :). I got the opposite end of what you were saying there obviously.

In which case is there a way I might separate the two off, what I want to do is do a specific action when a window is opened. Before I was doing this in init but I'm sure theres another way, maybe in :).

Thank you so much!

Mark.

robbieduncan
Mar 1, 2006, 05:01 PM
Are you single because I could kiss you ;). Finally it works. One thing I am noticing though is that the contents of the -init are being run twice, is this because there is now an instance in the MainMenu.nib as-well as the one bound too the main document window?

Man, i'll never doubt you again :). I got the opposite end of what you were saying there obviously.

In which case is there a way I might separate the two off, what I want to do is do a specific action when a window is opened. Before I was doing this in init but I'm sure theres another way, maybe in :).

Thank you so much!

Mark.

If you have an instance of the class in MainMenu and another in MyDocument each will be inited (and then awakeFromNibed). Every new document will cause another instance to be created as well. If this object is your application delegate it should only be in main Menu. If it has some sort of per-document functionality split that into another class and put that in the document nib.

Sorry to say I'm not much into kissing guys! Why do hot chicks never ask programming questions?

netytan
Mar 1, 2006, 05:12 PM
If you have an instance of the class in MainMenu and another in MyDocument each will be inited (and then awakeFromNibed). Every new document will cause another instance to be created as well. If this object is your application delegate it should only be in main Menu. If it has some sort of per-document functionality split that into another class and put that in the document nib.

Sorry to say I'm not much into kissing guys! Why do hot chicks never ask programming questions?

Mmmm Well, I'm not sure.

I don't have anything in my init as of yet but I was going to use it to preform actions performing actions when the window opens.

I can see your point about splitting the classes up however I'm using the document class to start my interpreter because I need to used make function call into the interpreter in the document then again I will probably need to do it in both nibs.

Is MainMenu loaded first?

Maybe it is best to put it in the MainMenu Nib what do you think honestly? I know there are a lot of questions but I like to do things in the best way I can.

LOL I'm not bothered about kissing boys though girls are better. It was an expression not a threat don't worry :).

Thanks a lot for all your help,

Mark.

robbieduncan
Mar 1, 2006, 05:21 PM
MainMenu should be loaded first (and only be loaded once). I'd use that for an app delegate which can respond to applicationWillTerminate.

If you need to start your interpreter per document then put the code to start it in the document class.

You can then use the applicationWillTerminate in the app delegate to loop over all documents (get a list from NSDocumentController) and call a method in the document class to stop the interpreter.

netytan
Mar 1, 2006, 07:27 PM
MainMenu should be loaded first (and only be loaded once). I'd use that for an app delegate which can respond to applicationWillTerminate.

If you need to start your interpreter per document then put the code to start it in the document class.

You can then use the applicationWillTerminate in the app delegate to loop over all documents (get a list from NSDocumentController) and call a method in the document class to stop the interpreter.

Thanks Robbie,

it took about 5 minutes and it works perfectly. I can now do something when the Apps loaded and do something when the windows load.

I'm currently initializing the interpreter in the initialize method because it only gets called once in the lifetime of the class. Would you say thats a good idea or not? It's not doing any harm so I'm tempted to just leave it and be 100%.

If the interpeter starts more than once then things could get very unstable because the interpreter doesn't return instances of itself it runs in the global scope, hence all the fuss about loading it at the beginning and closing it down at the very end :).

It occurs to me that I could now move the initialization to init. Would you recommend this?

Thanks a lot,

Mark.

robbieduncan
Mar 2, 2006, 06:05 AM
This is getting a bit confusing!

My understanding of what you are saying is this: you are writing a Cocoa document based app. You are using some sort of C based interpreter. You want to have a single instance of the interpreter shared amongst all documents? Thus you need to have an interpreter instance created at the start of the app and shutdown at the end.

So what I would do would be create a Cocoa wrapper for the interpreter that is a singleton object (this is reasonably easy). If the interpreter needs to be shutdown cleanly have a shutdown method in the wrapper can call it from the app delegate when the app is shutting down. If you want to initialise the interpreter when the app starts do it in the app delegate. Otherwise when the first document is created it will create the instance.

So you will have something like:

@interface InterpreterWrapper

+(InterpreterWrapper*) sharedInstance; // Whenever you want to get an instance of this class use this.
- (void) doInterpreterStuff; // Possibly more than one of these: do the interpreter stuff
- (void) shutdownInterpreter; // Call when the app is shutting down.

@end

Your implementation should look something like this

@implementation InterpreterWrapper

static InterpreterWrapper *wrapperSharedInstance;

+(InterpreterWrapper*) sharedInstance
{
if (wrapperSharedInstance==nil)
{
wrapperSharedInstance = [[InterpreterWrapper alloc] init];
}
return wrapperSharedInstance;
}

@end

For more details (including protecting init so as you can't break the patern) see this cocoadev page (http://www.cocoadev.com/index.pl?SingletonDesignPattern).

netytan
Mar 2, 2006, 11:11 AM
Actually the singleton class was the first thing I did and that works fine, what I wasn't sure about was weather or not to move it from the -initialize or -init in classed that use it :).

And then of course there was also the issue of not being able to shut the interpreter down but all good now.

I've decided to leave it where it is anyhow because it's being bound to a class method so it makes sense and adds that much extra security.

Thanks for all your help Robbie,

Mark.