In my NSApp delegate I add an observer of an object that is an NSWindow subclass that gets initiated in the delegate itself and that posts a notification once the window gets clicked. The selector is also in the delegate. From that same delegate class I initiate another object which when initiated adds itself as an observer for another window of the same NSWindow subclass of above and the selector is in this newly initiated class too. Both notifications get posted but the problem is that they get posted in both classes... Is this normal? I was hoping that it only got posted once.
@implementation AppController
- (id)init
{
if (self = [super init])
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(toggleTestWindow:) name: @"TestNotification" object: testWindow];
return self;
}
- (void)toggleTestWindow: (NSNotification *)aNotification
{
if (!testWindow) {
testWindow = [[MyWindow alloc] init];
[mainWindow addChildWindow: testWindow ordered: NSWindowAbove];
} else {
[mainWindow removeChildWindow: testWindow];
[testWindow orderOut: self];
[testWindow release];
testWindow = nil;
}
}
@end
I'm right about what I said in my comment on drawnonward's answer.
Variables are containers. A variable is distinct from the value that's in it. Generally, when you use the name of a variable in your code, you're actually referring to the value; when you say foo(bar)
, you're not passing the bar
variable itself to the foo
function, you're passing the value that's in the bar
variable.
Local variables aren't initialized to anything unless you initialize them. So, don't ever refer to a local variable without either assigning to it or initializing it previously. Random bad things will randomly happen.
Instance variables, on the other hand, are initialized to nil
, and will continue to contain nil
until you put something else in them. This matters because all through init
, you haven't put anything in the testWindow
instance variable, so it contains nil
.
Then, by saying addObserver:… selector:… name:… object:testWindow
, you pass that default value, nil
, as the object for which you want to observe for notifications. That translates to observing for that notification for any object.
That isn't what you meant, but what you meant isn't what you wrote. What you meant is to add yourself as an observer for the test window. But you haven't created the test window yet, and you haven't put its pointer in the testWindow
variable, so what you wrote is to add yourself as an observer for any object.
Only when the notification happens do you create the window (incorrectly, at that) and assign it to the variable. This is too late for it to have any effect on your observation; the assignment does not retroactively change how you're observing, because you could only pass what was in the variable at that time (which was nil
); you could not and cannot pass the variable nor any possible future values of the variable.
So, you need to create the window and assign to the variable in init
, then add yourself as an observer for the notification.
There are two correct ways to create a window in code. This is one of them, and this is the other. Don't use plain init
to create a window, because it will have no frame rectangle.
Or, better yet, instead of doing all this in code, just use IB to make the window. You'll need to make testWindow
an outlet and start observing in awakeFromNib
.
Either way, you have a problem on the other end, too, because you release
(and thereby destroy, or at least attempt to destroy) the window in your notification method. Don't expect to continue receiving notifications for an object after you destroy it. You need to move that release
message and nil
assignment out to someplace else in your code, to a place where you're truly finished with the window and not just hiding it temporarily.
In summary:
testWindow
variable in init
, before you send that addObserver:selector:name:object:
message.dealloc
.(Oh, and a style/maintanability matter: Don't sprinkle literal strings like @"TestNotification"
all over your code. Define a variable with that value somewhere, and use that variable everywhere you want to use the notification. Then, to change the string, you change it in exactly one place, and to rename the variable, you can use Xcode's Refactor tool.)