PDA

View Full Version : NSApplication::closeAll ?




zeppenwolf
Oct 26, 2010, 11:46 PM
Hi.

What is the most appropriate way to hook into the 'closeAll' command?

For now, I have implemented

- (void) closeAll: (id) sender;

in MyDerivedFromNSApplication class... And it works fine, but I have just one "problem": I can't find this method declared anywhere in any header. I only implemented the above after seeing "NSApplication closeAll:" in the call chain in the debugger. So in fact, I'm only guessing about the "(id) sender" part, and since (id) means almost nothing, I can't even be sure I'm right...

In other words, I think I'm cheating. (???) I'm *guessing* that closeAll exists in the system compile-ware, but that us public peeps are not supposed to have access to it...? ( I also tried overriding sendAction:, and then testing the SEL==closeAll, and that works, but it seems goofy if I can override closeAll: directly... )

Is it clear? I'm only just learning this cocoa stuff... Thanks. :)



Sydde
Oct 27, 2010, 12:54 AM
Hi.

What is the most appropriate way to hook into the 'closeAll' command?

For now, I have implemented

- (void) closeAll: (id) sender;

in MyDerivedFromNSApplication class... And it works fine, but I have just one "problem": I can't find this method declared anywhere in any header. I only implemented the above after seeing "NSApplication closeAll:" in the call chain in the debugger. So in fact, I'm only guessing about the "(id) sender" part, and since (id) means almost nothing, I can't even be sure I'm right...

In other words, I think I'm cheating. (???) I'm *guessing* that closeAll exists in the system compile-ware, but that us public peeps are not supposed to have access to it...? ( I also tried overriding sendAction:, and then testing the SEL==closeAll, and that works, but it seems goofy if I can override closeAll: directly... )

Is it clear? I'm only just learning this cocoa stuff... Thanks. :)

I would consider using NSLog to see if you can figure out what sender actually is (bearing in mind that it might be a primitive or some other kind of pointer). Keep in mind that you have no idea whether it wants a return value: if the app starts to exhibit weird behavior on quit, this could be the cause (I would try returning nil to be safe).

Also, I would seriously consider ending the method with a call to super (as you would with -dealloc) once you determine what kind of parameter it is.

zeppenwolf
Oct 27, 2010, 10:59 PM
I would consider using NSLog to see if you can figure out what sender actually is

IF the function is declared as I listed, then I don't think that determining what class 'sender' is in a specific case would have any relevance. EG, supposed I use the debugger to figure out that the parameter was an object of SomeClass...

So what? That wouldn't preclude that at some other time the object passed could be SomeOtherClass... See? Since everything is an 'id', I wouldn't be able to conclude anything by seeing a particular class passed. ( Only if I observed TWO types passed which had NOTHING in common except for id, in which case I could conclude that id was correct ).
Keep in mind that you have no idea whether it wants a return value
Well, I DO think I know the return value! In fact, that's the *only* thing I feel pretty confident about, because if I had that wrong, then my function of a derived class would not be called *in place of* the same function in the base class. Right?
Also, I would seriously consider ending the method with a call to super
Absolutely-- I do. And as far as that goes, it doesn't matter *what* the actual type of the parameter is, eh? Whatever it is, I pass it to super just as I got it, (after doing my massaging...)

Anyway, thanks, Sydde for responding. I take it that on the central question, "how would you do this?" you don't have an opinion?

lee1210
Oct 28, 2010, 12:10 AM
Riddle me this: what are you doing in your version of closeAll:? What special behavior must be added here for your custom class? There are 4 methods that are suggested for overriding, and one specific one that you're basically told to never override. This method isn't any of those, but since it's undocumented it's likely "internal" to the class and not to be mucked with. Were I in your position I would leave the default behavior alone. If it's pure curiosity you could just print the type of thing passed in so you can observe this.

-Lee

Edit: as for your question regarding return type and if getting it wrong will result in super's implementation being called: it will not. Method names don't include type. The name of this method is closeAll:. If you implement a method with any return type and any argument type you've implemented this method.

zeppenwolf
Oct 28, 2010, 11:21 PM
Riddle me this: what are you doing in your version of closeAll:?

( Does that make me Batman ? ) There are actually two separate reasons why I need to override the closeAll behavior, (in one app!), but I'll just describe the most important:

One of my windows has an appearance which is dependent on the existence or absence of another (specific) window.

If the Elephant window appears, the Monkey window adjusts itself.
If the Elephant window closes, the Monkey window adjusts itself.

The dependence is only in one direction:

If the Monkey window appears, or closes, the Elephant window could not give a Rat's patooty.

When the closeAll method occurs, if the Elephant window closes *before* the Monkey window, (which for some reason happens every time!), then there will be a brief moment where the Monkey window adjusts itself before closing.

It is a momentary thing, but noticeable.

If I DO NOT override the closeAll behavior, then to the idealized user, this is clearly a small but noticeable wasted effort. To the user, the Monkey window has no valid need to adjust itself, since it is destined to die. The user would have every right to think that the programmer was negligent.

If I DO override the closeAll behavior, ( forcing the Monkey window to close BEFORE the Elephant window ), then of course there is no such blip. And in fact, without that blip, all open windows close in the blink of an eye, so the user sees only what he exactly expects to see-- every open window disappears, you can't even tell who closed before whom.


So what do you say? Does it seem worthy?


There are 4 methods that are suggested for overriding, and one specific one that you're basically told to never override.

I would be grateful if you are willing to expand on that.

Were I in your position I would leave the default behavior alone.

Excuse me, but I think you made that statement without thinking about it. I think that if you were in my position, you would have fixed the problem that I'm asking about in a few minutes, and you have gone on to the next thing... Do you disagree?

Method names don't include type.

Well that's fascinating, thank you. I've compiled and run the app with closeAll returning a bool and an NSString*... It all works fine, for some reason... This is one hell of a language, that's for sure! :) Anyway, I stand corrected: only the *name* and the *number* of arguments is relevant... (right???)

lee1210
Oct 29, 2010, 08:48 AM
( Does that make me Batman ? ) There are actually two separate reasons why I need to override the closeAll behavior, (in one app!), but I'll just describe the most important:

One of my windows has an appearance which is dependent on the existence or absence of another (specific) window.

If the Elephant window appears, the Monkey window adjusts itself.
If the Elephant window closes, the Monkey window adjusts itself.

The dependence is only in one direction:

If the Monkey window appears, or closes, the Elephant window could not give a Rat's patooty.

When the closeAll method occurs, if the Elephant window closes *before* the Monkey window, (which for some reason happens every time!), then there will be a brief moment where the Monkey window adjusts itself before closing.

It is a momentary thing, but noticeable.

If I DO NOT override the closeAll behavior, then to the idealized user, this is clearly a small but noticeable wasted effort. To the user, the Monkey window has no valid need to adjust itself, since it is destined to die. The user would have every right to think that the programmer was negligent.

If I DO override the closeAll behavior, ( forcing the Monkey window to close BEFORE the Elephant window ), then of course there is no such blip. And in fact, without that blip, all open windows close in the blink of an eye, so the user sees only what he exactly expects to see-- every open window disappears, you can't even tell who closed before whom.


So what do you say? Does it seem worthy?


It sounds like a sticky wicket, to be sure. I definitely feel like "making it work" is very important, but if you override an undocumented method any OS update could break this. It likely won't in this case, but you're treading on dangerous ground. Not that you are targeting this specifically, but using undocumented methods like this will probably also result in an app being rejected from the app store. In any event, I'm not trying to judge the worthiness of your use, I was just not clear that there was a particular purpose to overriding this or if it was an academic exercise.


I would be grateful if you are willing to expand on that.

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html%23//apple_ref/doc/uid/20000012-800375

From that page, run, sendEvent:,requestUserAttention:,targetForAction: are the methods recommended for subclassing. sharedApplication is specifically forbidden due to its complexity.


Excuse me, but I think you made that statement without thinking about it. I think that if you were in my position, you would have fixed the problem that I'm asking about in a few minutes, and you have gone on to the next thing... Do you disagree?


I didn't know that you were needing specific functionality that required alteration of the behavior of this method at the time, I thought this was a matter of curiosity not practicality. Off the top of my head I don't know the right place to put this kind of handling, so I'm actually not sure what I'd do in your situation. I guess I would try to work out some sort of chain in "windowWillClose:" to, say, check if i am the elephant window, and if so, if the monkey window is open, close it, then close myself. If the monkey window is already closed, just proceed with closing as normal. Maybe this won't work for you, I haven't tried it. Just tossing some ideas out.


Well that's fascinating, thank you. I've compiled and run the app with closeAll returning a bool and an NSString*... It all works fine, for some reason... This is one hell of a language, that's for sure! :) Anyway, I stand corrected: only the *name* and the *number* of arguments is relevant... (right???)

The "name" of a method (in this context a "selector", the identifier used to search for a method) consists of the "main name" that comes first, any colons, and any "labels" for parameters between colons. From http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/ObjC.pdf page 16:

A selector name includes all the parts of the name, including the colons, so the method is named setOriginX:y:. It has two colons as it takes two arguments. The selector name does not, however, include anything else, such as return type or parameter types.

This behavior closely mimics C's behavior in that a method is only its name, the argument types and return type are up to you to manage. Some compilers are nice and will warn you about mismatched argument numbers and types, but with return type it's pretty tough because of implicit casts on assignment. Anyways, C++ takes a very different approach to this by allowing "overloading" of methods, allowing a method with the same name to take different types or numbers of arguments and having a separate implementation for each. You cannot, however, have a method with the same name, same type and number of arguments, but a different return type.

The advantage over the C way is that at least with the :s being part of the name, the number of arguments is at least explicit.

-Lee

Sydde
Oct 29, 2010, 03:32 PM
( Does that make me Batman ? ) There are actually two separate reasons why I need to override the closeAll behavior, (in one app!), but I'll just describe the most important:

One of my windows has an appearance which is dependent on the existence or absence of another (specific) window.

In this particular case, there is a much easier and more ergonomically/aesthetically sensible approach (IMO). Just set an Application delegate which implements the -applicationWillTerminate: method. Then you can control the closing order for the windows.

I have no advice on overriding an undocumented method other than to say, ask yourself "how else might this be handled?"

zeppenwolf
Oct 31, 2010, 01:10 PM
Ok guys, I feel like I've done my due diligence-- I followed the links you gave me, Lee, and beyond, I googled myself silly, and I thunk about it real hard...

Anyway, for posterity or something, here's what I'm going to settle for:


bool gClosingAllB = false; // global used by wins to know close vs closeAll

// MyDerivedFromNSApp::

- (BOOL)sendAction:(SEL)theAction to:(id)theTarget from:(id)sender {

if ( theAction != @selector( closeAll: ) )
return [super sendAction:theAction to:theTarget from:sender];

gClosingAllB = true;

bool tempB = [super sendAction:theAction to:theTarget from:sender];

gClosingAllB = false;

return tempB;
}
Kind of an anticlimax, I suppose. I DO of course manage to avoid overriding the *super secret* closeAll function-- I just detect it's imminent use... Compared to overriding closeAll directly this is kinda ugly to me, but the thing that really bugs me is how you mentioned, Lee, about the App store maybe rejecting my app... That never occurred to me!

I'm thinking of that Carbon Dater program... remember? If Apple uses a similar little thing, which just detects certain values in the binary, then my app might get rejected *anyway*, since the final binary will have a pointer to the super secret closeAll method, even though I don't actually call it or override it...

Ah well. If that happens, I'll deal with it then. For now, if anyone wants to file a last minute stay of execution, (what a bad pun!), speak now or hold your peas. Otherwise, I'm saying thanks to you guys and this issue is closed. Maybe in the future Apple will trust us enough to make closeAll legal...