NSApplication terminate: behavior

  • Hello,

    I have noticed that NSApplication's run method does not return and call
    exit() instead. Posts on this list and other places suggest to do
    cleanup in applicationWillTerminate: or an atexit()-registered callback
    function. However this does not do what I want it to...
    The native Objective-C run method is being called from a managed
    context and the ObjC runtime / AppKit framework exiting the app does
    not let Garbage Collection happen for the managed context. If from a
    managed context I just call the CLR exit() equivalent
    System.Environment.Exit(0), it does a GC run but does not return to my
    Main method either, and the menu item stays selected and my window
    visible for some seconds after the application is Exit'ed.

    Calling stop: from applicationShouldTerminate: returning ...Cancel
    returns from run to Main but just as Exit() the Cocoa termination
    cleanup is not performed correctly.

    And when I throw an Exception in the atexit handler it does not go back
    through the callstack, probably because all native instances are
    already dealloc'ed...

    So is there some geeky way to write a run equivalent that does not exit
    but instead return? What does terminate: do exactly anyway apart from
    calling the delegates? Any docs on that part?

    Best regards,

    Andreas
  • What do you want to do that you can't do in -applicationWillTerminate:?

    -jcr
  • Hi John,

    > What do you want to do that you can't do in -applicationWillTerminate:?

    I said so: I want both the managed runtime (Mono) to do a final,
    complete garbage collection run (shutdown) and native Cocoa to shut
    down in a normal way.

    Right now either way it's only one of them that gets shut down
    correctly.

    If I exit Mono in -applicationWillTerminate: then Mono exits fine,
    calling my managed finalizers, but Cocoa does not, as described, with
    the display not being updated for some time and the windows etc. not
    being removed.

    It is simply not good behavior for native code to exit a managed Mono
    or whatever runtime through a call to exit(). How does Java handle
    this? Does it face the same problem or does it return to its main?

    Andreas
  • On 20/gen/06, at 02:26, Andreas Färber wrote:

    > I have noticed that NSApplication's run method does not return and
    > call exit() instead. Posts on this list and other places suggest to
    > do cleanup in applicationWillTerminate: or an atexit()-registered
    > callback function. However this does not do what I want it to...
    > The native Objective-C run method is being called from a managed
    > context and the ObjC runtime / AppKit framework exiting the app
    > does not let Garbage Collection happen for the managed context. If
    > from a managed context I just call the CLR exit() equivalent
    > System.Environment.Exit(0), it does a GC run but does not return to
    > my Main method either, and the menu item stays selected and my
    > window visible for some seconds after the application is Exit'ed.
    >
    > Calling stop: from applicationShouldTerminate: returning ...Cancel
    > returns from run to Main but just as Exit() the Cocoa termination
    > cleanup is not performed correctly.
    >
    > And when I throw an Exception in the atexit handler it does not go
    > back through the callstack, probably because all native instances
    > are already dealloc'ed...
    >
    > So is there some geeky way to write a run equivalent that does not
    > exit but instead return?

    The problem is not with run: you can break out of it by calling stop.
    It's terminate that calls exit. What you should do is call stop
    instead of terminate, and reproduce the functionality of terminate
    after run exits, by calling delegate methods as outlined in the
    documentation.

    > What does terminate: do exactly anyway apart from calling the
    > delegates? Any docs on that part?

    What exactly is not working for you, once you call all delegate
    methods correctly?

    Camillo _______________________________________________
    Do not post admin requests to the list. They will be ignored.
    Cocoa-dev mailing list      (<Cocoa-dev...>)
    Help/Unsubscribe/Update your Subscription:
    http://lists.apple.com/mailman/options/cocoa-dev/<cocoa...>

    This email sent to <cocoa...>
  • Hello Camillo,

    > On 20/gen/06, at 02:26, Andreas Färber wrote:
    >
    >> I have noticed that NSApplication's run method does not return and
    >> call exit() instead. Posts on this list and other places suggest to
    >> do cleanup in applicationWillTerminate: or an atexit()-registered
    >> callback function. However this does not do what I want it to...
    >> The native Objective-C run method is being called from a managed
    >> context and the ObjC runtime / AppKit framework exiting the app does
    >> not let Garbage Collection happen for the managed context. If from a
    >> managed context I just call the CLR exit() equivalent
    >> System.Environment.Exit(0), it does a GC run but does not return to
    >> my Main method either, and the menu item stays selected and my window
    >> visible for some seconds after the application is Exit'ed.
    >>
    >> Calling stop: from applicationShouldTerminate: returning ...Cancel
    >> returns from run to Main but just as Exit() the Cocoa termination
    >> cleanup is not performed correctly.
    >>
    >> And when I throw an Exception in the atexit handler it does not go
    >> back through the callstack, probably because all native instances are
    >> already dealloc'ed...
    >>
    >> So is there some geeky way to write a run equivalent that does not
    >> exit but instead return?
    >
    > The problem is not with run: you can break out of it by calling stop.
    > It's terminate that calls exit. What you should do is call stop
    > instead of terminate, and reproduce the functionality of terminate
    > after run exits, by calling delegate methods as outlined in the
    > documentation.

    Well from my view the problem is run not returning as I am not calling
    terminate: myself. ;-)

    I did try calling stop: from applicationShouldTerminate: returning
    NS***Cancel and, like I said in the above quote, it worked in taking me
    back to my Main method. However I did not find an easy way to terminate
    (as opposed to "terminate:") the Cocoa NSApplication, leaving the
    windows, application menu etc. on screen until after my main
    application was already exited.

    >
    >> What does terminate: do exactly anyway apart from calling the
    >> delegates? Any docs on that part?
    >
    > What exactly is not working for you, once you call all delegate
    > methods correctly?

    Maybe I do not understand the concept of Cocoa delegates correctly? I
    myself have set up for testing purposes an object using setDelegate: on
    which to receive the applicationShould/WillTerminate: messages. Why
    should I send those messages to myself from my own Main? Would sending
    such messages also hide and release my windows as a nice side-effect?!

    If I knew what exactly terminate: was doing *apart from calling my
    delegates* - the latter being well documented, I know, I've read it or
    I would not be receiving them :-) - then I could send those messages
    myself, whether manually from my Main method or maybe implicitly by
    posing as NSApplication.

    Regards,

    Andreas

    P.S. Callling my System.Environment.Exit() from the atexit() callback
    btw shows some unexpected output and ultimately crashes, thus is not
    usable.
  • On 20/gen/06, at 21:12, Andreas Färber wrote:

    > Hello Camillo,
    >
    >> On 20/gen/06, at 02:26, Andreas Färber wrote:
    >>
    >>> I have noticed that NSApplication's run method does not return
    >>> and call exit() instead. Posts on this list and other places
    >>> suggest to do cleanup in applicationWillTerminate: or an atexit()-
    >>> registered callback function. However this does not do what I
    >>> want it to...
    >>> The native Objective-C run method is being called from a managed
    >>> context and the ObjC runtime / AppKit framework exiting the app
    >>> does not let Garbage Collection happen for the managed context.
    >>> If from a managed context I just call the CLR exit() equivalent
    >>> System.Environment.Exit(0), it does a GC run but does not return
    >>> to my Main method either, and the menu item stays selected and my
    >>> window visible for some seconds after the application is Exit'ed.
    >>>
    >>> Calling stop: from applicationShouldTerminate:
    >>> returning ...Cancel returns from run to Main but just as Exit()
    >>> the Cocoa termination cleanup is not performed correctly.
    >>>
    >>> And when I throw an Exception in the atexit handler it does not
    >>> go back through the callstack, probably because all native
    >>> instances are already dealloc'ed...
    >>>
    >>> So is there some geeky way to write a run equivalent that does
    >>> not exit but instead return?
    >>
    >> The problem is not with run: you can break out of it by calling
    >> stop. It's terminate that calls exit. What you should do is call
    >> stop instead of terminate, and reproduce the functionality of
    >> terminate after run exits, by calling delegate methods as outlined
    >> in the documentation.
    >
    > Well from my view the problem is run not returning as I am not
    > calling terminate: myself. ;-)

    You mean you are not in control of the Cocoa application?

    > I did try calling stop: from applicationShouldTerminate: returning
    > NS***Cancel and, like I said in the above quote, it worked in
    > taking me back to my Main method. However I did not find an easy
    > way to terminate (as opposed to "terminate:") the Cocoa
    > NSApplication, leaving the windows, application menu etc. on screen
    > until after my main application was already exited.

    I think stop just sets a flag that tells run to stop looping; if you
    call it in applicationShouldTerminate, you're already inside
    terminate and exit is going to be called anyway. You need to prevent
    terminate from being called, or replace the default implementation
    with something that calls stop instead.

    >>> What does terminate: do exactly anyway apart from calling the
    >>> delegates? Any docs on that part?
    >>
    >> What exactly is not working for you, once you call all delegate
    >> methods correctly?
    >
    > Maybe I do not understand the concept of Cocoa delegates correctly?
    > I myself have set up for testing purposes an object using
    > setDelegate: on which to receive the applicationShould/
    > WillTerminate: messages. Why should I send those messages to myself
    > from my own Main? Would sending such messages also hide and release
    > my windows as a nice side-effect?!
    >
    > If I knew what exactly terminate: was doing *apart from calling my
    > delegates* - the latter being well documented, I know, I've read it
    > or I would not be receiving them :-) - then I could send those
    > messages myself, whether manually from my Main method or maybe
    > implicitly by posing as NSApplication.

    Are you saying that you already implemented everything the
    documentation says (calling app delegate methods, document-based app
    methods, sending the notification, all in the right order), and
    things still aren't getting cleaned up correctly?
    There's also the possibility that it's not a Cocoa problem at all.
    For example, AppKit might not be destroying windows when the app
    quit, instead relying on a lower layer mechanism (such as an atexit
    callback that tells the window server that the app is going down, so
    it can destroy all of its windows?).

    However, the important application-specific cleanup is done inside
    delegate methods. As long as you're calling them as stated in the
    documentation, documents will be closed, preferences will be saved, etc.

    Camillo _______________________________________________
    Do not post admin requests to the list. They will be ignored.
    Cocoa-dev mailing list      (<Cocoa-dev...>)
    Help/Unsubscribe/Update your Subscription:
    http://lists.apple.com/mailman/options/cocoa-dev/<cocoa...>

    This email sent to <cocoa...>
  • Am 20.01.2006 um 22:15 schrieb Camillo Lugaresi:

    >> from my view the problem is run not returning as I am not calling
    >> terminate: myself. ;-)
    >
    > You mean you are not in control of the Cocoa application?

    What I mean is this: My bundle starts up the Mono executable via a
    launch script and symlink and loads my application. My class library
    then calls NSApplicationLoad() to load AppKit as a dynamic library
    sort-of, loads the menu from a nib and sets up the main window; when I
    am done with initializing I call [[NSApplication sharedApplication]
    run]. I then at some point quit the application via the menu command in
    the application menu (or its shortcut). I do not have control over Mono
    shutdown.
    Because of the circumstance that Cocoa was not started up as a native
    application, whenever the application decides to exit I would wish to
    have it return control back to the application that loaded it so that
    it can shut down gracefully. My managed code is for instance using
    notification web services as part of an object model that would need to
    be unregistered before the app terminates and its sockets "disappear";
    this is not really exposed in the object model and shouldn't be and
    works fine on Windows with the objects being finalized.

    >
    >> I did try calling stop: from applicationShouldTerminate: returning
    >> NS***Cancel and, like I said in the above quote, it worked in taking
    >> me back to my Main method. However I did not find an easy way to
    >> terminate (as opposed to "terminate:") the Cocoa NSApplication,
    >> leaving the windows, application menu etc. on screen until after my
    >> main application was already exited.
    >
    > I think stop just sets a flag that tells run to stop looping; if you
    > call it in applicationShouldTerminate, you're already inside terminate
    > and exit is going to be called anyway. You need to prevent terminate
    > from being called,

    a) If in ***shouldTerminate I return ***Cancel
    (NSApplicationTerminateReplyCancel?) it does not terminate;
    b ) if I return *Now it terminates immediately, not returning to Main
    as usual, and
    c) if I return *Later the main loop is halted, not returning to Main.
    If I call stop: before I return Cancel, I get out of the run loop and
    back to Main but the application is still alive, just no runloop
    active.

    > or replace the default implementation with something that calls stop
    > instead.

    I guess I would be able call stop: from a posed-as terminate: method
    but I would have to know what to do there. :-(

    >
    >>>> What does terminate: do exactly anyway apart from calling the
    >>>> delegates? Any docs on that part?
    >>>
    >>> What exactly is not working for you, once you call all delegate
    >>> methods correctly?
    >>
    >> Maybe I do not understand the concept of Cocoa delegates correctly? I
    >> myself have set up for testing purposes an object using setDelegate:
    >> on which to receive the applicationShould/WillTerminate: messages.
    >> Why should I send those messages to myself from my own Main? Would
    >> sending such messages also hide and release my windows as a nice
    >> side-effect?!
    >>
    >> If I knew what exactly terminate: was doing *apart from calling my
    >> delegates* - the latter being well documented, I know, I've read it
    >> or I would not be receiving them :-) - then I could send those
    >> messages myself, whether manually from my Main method or maybe
    >> implicitly by posing as NSApplication.
    >
    > Are you saying that you already implemented everything the
    > documentation says (calling app delegate methods, document-based app
    > methods, sending the notification, all in the right order), and things
    > still aren't getting cleaned up correctly?
    > There's also the possibility that it's not a Cocoa problem at all. For
    > example, AppKit might not be destroying windows when the app quit,
    > instead relying on a lower layer mechanism (such as an atexit callback
    > that tells the window server that the app is going down, so it can
    > destroy all of its windows?).
    >
    > However, the important application-specific cleanup is done inside
    > delegate methods. As long as you're calling them as stated in the
    > documentation, documents will be closed, preferences will be saved,
    > etc.

    Could you please explain this further? What delegates exactly are you
    referring to? The AppKit NSApplication documentation says this about
    terminate:

    <<This method is typically invoked when the user chooses Quit or Exit
    from the application’s menu. Each use of terminate: invokes
    applicationShouldTerminate: (page 144) to notify the delegate that the
    application will terminate. If applicationShouldTerminate: (page 144)
    returns NO, control is returned to the main event loop, and the
    application isn’t terminated. Otherwise, the document controller of the
    application (if one exists) is asked to check its documents and, if
    there are unsaved changes, ask the user to save those changes. Then,
    this method posts an NSApplicationWillTerminateNotification (page 147)
    to the default notification center. Don’t put final cleanup code in
    your application’s main() function—it will never be executed. If
    cleanup is necessary, have the delegate respond to
    applicationWillTerminate: (page 145) and performcleanup in that method.
    >>

    The YES or NO is actually replaced by the three enum values referenced
    somewhere above, as specified in the delegates' documentation.

    Now are these the two delegates that you say I should call myself?
    Don't they just enable *me* to react to these events? And how exactly
    would I invoke them anyway?

    Right now when experimenting with stop: I do not know what exactly to
    do from there! That's one of the questions. The documentation does not
    say "call this, do that", the above quote and other places just
    describe that terminate: is calling the two mentioned delegates, but
    does not say anything else it might be doing.

    And to make it very clear I do not want to call anything from my Main
    method that again would result in a call to exit() on the side of
    Cocoa. Just close and release the windows, views, controls, menu and
    let me return to my NSAutoreleasePool wrapper and other managed
    non-Cocoa objects.

    Thanks for your help,

    Andreas
  • On 20/gen/06, at 23:23, Andreas Färber wrote:
    >
    > Am 20.01.2006 um 22:15 schrieb Camillo Lugaresi:
    >
    >>> I did try calling stop: from applicationShouldTerminate:
    >>> returning NS***Cancel and, like I said in the above quote, it
    >>> worked in taking me back to my Main method. However I did not
    >>> find an easy way to terminate (as opposed to "terminate:") the
    >>> Cocoa NSApplication, leaving the windows, application menu etc.
    >>> on screen until after my main application was already exited.
    >>
    >> I think stop just sets a flag that tells run to stop looping; if
    >> you call it in applicationShouldTerminate, you're already inside
    >> terminate and exit is going to be called anyway. You need to
    >> prevent terminate from being called,
    >
    > a) If in ***shouldTerminate I return ***Cancel
    > (NSApplicationTerminateReplyCancel?) it does not terminate;
    > b ) if I return *Now it terminates immediately, not returning to
    > Main as usual, and
    > c) if I return *Later the main loop is halted, not returning to Main.
    > If I call stop: before I return Cancel, I get out of the run loop
    > and back to Main but the application is still alive, just no
    > runloop active.

    Ah, righ it makes sense that calling stop and then cancelling the
    termination attempt gets you out of the run loop. I must have misread
    you before.

    >> Are you saying that you already implemented everything the
    >> documentation says (calling app delegate methods, document-based
    >> app methods, sending the notification, all in the right order),
    >> and things still aren't getting cleaned up correctly?
    >> There's also the possibility that it's not a Cocoa problem at all.
    >> For example, AppKit might not be destroying windows when the app
    >> quit, instead relying on a lower layer mechanism (such as an
    >> atexit callback that tells the window server that the app is going
    >> down, so it can destroy all of its windows?).
    >>
    >> However, the important application-specific cleanup is done inside
    >> delegate methods. As long as you're calling them as stated in the
    >> documentation, documents will be closed, preferences will be
    >> saved, etc.
    >
    > Could you please explain this further? What delegates exactly are
    > you referring to? The AppKit NSApplication documentation says this
    > about terminate:
    >
    > <<This method is typically invoked when the user chooses Quit or
    > Exit from the application’s menu. Each use of terminate: invokes
    > applicationShouldTerminate: (page 144) to notify the delegate that
    > the application will terminate. If applicationShouldTerminate:
    > (page 144) returns NO, control is returned to the main event loop,
    > and the application isn’t terminated. Otherwise, the document
    > controller of the application (if one exists) is asked to check its
    > documents and, if there are unsaved changes, ask the user to save
    > those changes. Then, this method posts an
    > NSApplicationWillTerminateNotification (page 147) to the default
    > notification center. Don’t put final cleanup code in your
    > application’s main() function—it will never be executed. If cleanup
    > is necessary, have the delegate respond to
    > applicationWillTerminate: (page 145) and performcleanup in that
    > method. >>
    >
    > The YES or NO is actually replaced by the three enum values
    > referenced somewhere above, as specified in the delegates'
    > documentation.
    >
    > Now are these the two delegates that you say I should call myself?
    > Don't they just enable *me* to react to these events?

    Yes, although I think there might be other parts of Cocoa that depend
    on the NSApplicationWillTerminateNotification. I was under the
    impression that you were trying to load an existing Cocoa
    application, in which case ensuring that delegate methods are called
    as specified in the docs is crucial.

    > And how exactly would I invoke them anyway?

    For example,

    delegate = [NSApp delegate];
    if (delegate) reply = [delegate applicationShouldTerminate:NSApp];

    ...etc. Use NSNotificationCenter to send the
    NSApplicationWillTerminateNotification.
    You should probably also go throught the window list and close each
    window, eg:

    NSArray *windows = [NSApp windows];
    unsigned count = [windows count];

    while (count--) {
      [[windows objectAtIndex:count] close];
    }

    Finally, I guess you can try calling [NSApp release];

    > Right now when experimenting with stop: I do not know what exactly
    > to do from there! That's one of the questions. The documentation
    > does not say "call this, do that", the above quote and other places
    > just describe that terminate: is calling the two mentioned
    > delegates, but does not say anything else it might be doing.

    Yes, I don't see the details are documented anywhere. However, since
    you have full control of the application, you have the advantage that
    you don't have to worry about all possible side-effects that might
    not happen, but only the ones which are important to you.

    > And to make it very clear I do not want to call anything from my
    > Main method that again would result in a call to exit() on the side
    > of Cocoa. Just close and release the windows, views, controls, menu
    > and let me return to my NSAutoreleasePool wrapper and other managed
    > non-Cocoa objects.

    The things I outlined above should be a start. Once you have
    implemented them, see what's missing.
    Unfortunately, I think it will have to be a process of trial and
    error unless someone who knows Cocoa's internals decides to shed some
    more light on the termination process.

    You could also consider stopping the application right before
    terminate is called, then setting a breakpoint on objc_msgsend (and
    the other versions of that call) that logs each method call and then
    continues. That should give you a log of the methods called during
    terminate, which might be useful.

    Camillo _______________________________________________
    Do not post admin requests to the list. They will be ignored.
    Cocoa-dev mailing list      (<Cocoa-dev...>)
    Help/Unsubscribe/Update your Subscription:
    http://lists.apple.com/mailman/options/cocoa-dev/<cocoa...>

    This email sent to <cocoa...>
  • Am 21.01.2006 um 00:01 schrieb Camillo Lugaresi:

    > Use NSNotificationCenter to send the
    > NSApplicationWillTerminateNotification.
    > You should probably also go throught the window list and close each
    > window, eg:
    >
    > NSArray *windows = [NSApp windows];
    > unsigned count = [windows count];
    >
    > while (count--) {
    > [[windows objectAtIndex:count] close];
    > }
    >
    > Finally, I guess you can try calling [NSApp release];

    Closing the windows was a great tip! The trick here is to hide the
    application first, otherwise I get the spinning color wheel and related
    problems I mentioned.
    I'm also sending a "NSApplicationWillTerminate" notification but I
    didn't notice any difference.

    Haven't got it fully working, but I'm a step further. :-)

    Thanks,

    Andreas
previous month january 2006 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
Go to today