Cocoa 多个对象上的通知

Cocoa 多个对象上的通知,cocoa,nsnotifications,Cocoa,Nsnotifications,在我的NSApp委托中,我添加了一个对象的观察者,该对象是NSWindow子类,在委托本身中启动,并在单击窗口后发布通知。选择器也位于委托中。从同一个委托类中,我启动了另一个对象,该对象在启动时将自身添加为上述同一NSWindow子类的另一个窗口的观察者,选择器也在这个新启动的类中。两个通知都会被发布,但问题是它们在两个类中都会被发布。。。这正常吗?我希望它只被张贴一次 @implementation AppController - (id)init { if (self = [supe

在我的NSApp委托中,我添加了一个对象的观察者,该对象是NSWindow子类,在委托本身中启动,并在单击窗口后发布通知。选择器也位于委托中。从同一个委托类中,我启动了另一个对象,该对象在启动时将自身添加为上述同一NSWindow子类的另一个窗口的观察者,选择器也在这个新启动的类中。两个通知都会被发布,但问题是它们在两个类中都会被发布。。。这正常吗?我希望它只被张贴一次

@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

NSN通知可以按名称和实例进行筛选。为每个通知选择不同的名称,或者将每个观察者注册到它想要观察的特定对象实例。选择器只告诉通知中心,一旦确定观察者需要特定的通知,该调用什么方法


注册观察者时,将要侦听的实例作为对象参数传递。当您发布来自该实例的通知时,将self作为对象传递。

我在对drawnonward回答的评论中所说的是对的

变量是容器。变量与其中的值不同。通常,当您在代码中使用变量名时,实际上是指该值;当您说
foo(bar)
时,您不是将
bar
变量本身传递给
foo
函数,而是传递
bar
变量中的值

除非初始化局部变量,否则不会将其初始化为任何内容。因此,在没有赋值或初始化局部变量之前,不要引用它。随机的坏事会随机发生

另一方面,实例变量初始化为
nil
,并将继续包含
nil
,直到您在其中放入其他内容。这很重要,因为在整个
init
过程中,您没有在
testWindow
实例变量中放入任何内容,因此它包含
nil

然后,通过说
addObserver:…selector:…name:…object:testWindow
,您将默认值
nil
传递为要观察其通知的对象。这转化为观察任何对象的通知

这不是你的意思,但你的意思不是你写的。您的意思是将自己添加为测试窗口的观察者。但是您还没有创建测试窗口,也没有将其指针放在
testWindow
变量中,因此您编写的是将自己添加为任何对象的观察者

只有当通知发生时,您才会创建窗口(错误地创建窗口)并将其分配给变量。这太晚了,它对你的观察没有任何影响;赋值不会追溯改变您观察的方式,因为您只能传递当时变量中的内容(即
nil
);您不能也不能传递变量或变量的任何可能的未来值

因此,您需要创建窗口并在
init
中指定变量,然后将自己添加为通知的观察者

在代码中创建窗口有两种正确的方法。是其中一个,也是另一个。不要使用plain
init
创建窗口,因为它没有框架矩形

或者,更好的做法是,不用在代码中完成所有这些,而是使用IB来创建窗口。您需要将
testWindow
设置为一个出口,并在
awakeFromNib
中开始观察

无论哪种方式,另一端也有问题,因为您
释放了通知方法中的窗口(并因此销毁,或至少尝试销毁)。不要期望在销毁对象后继续接收该对象的通知。您需要将
release
消息和
nil
分配移到代码中的其他地方,移到您真正完成窗口操作的地方,而不仅仅是暂时隐藏它

总之:

  • 在发送
    addObserver:selector:name:object:
    消息之前,创建窗口并将其指针指定给
    init
    中的
    testWindow
    变量
  • 对于用户可以显示和隐藏的窗口,请将窗口的生存期与其显示/隐藏状态分开。它可以有一个窗口对象的命令了;你不需要在订购后马上销毁它。内存不再那么稀缺了,不管怎样,Mac电脑上的内存也不再那么稀缺了。仅当您真正完成此操作时才释放窗口,可能只是在
    dealloc

  • (哦,还有一个风格/可维护性问题:不要散布像
    @“TestNotification”这样的文字字符串)
    所有代码。在某个地方定义一个具有该值的变量,并在您想要使用通知的任何地方使用该变量。然后,要更改字符串,您只需在一个地方更改它,要重命名该变量,您可以使用Xcode的重构工具。)

    这正是我正在做的(除了按名称过滤它之外)。两个观察者都在两个不同的IBoutlet注册,这两个IBoutlet最初为零,但在发布通知时(不一定是在单击窗口时),选择器会启动它们,并在稍后再次发布NSNotification时取消分配它们。。。我这样做是为了在通知发布时可以释放IBOutlets,而不知道窗口是从哪个IBOutlets内部发出的,但我确实从可以释放它的选择器中知道它。简·亨德里克斯:IBOutlets只是变量。当您说
    object:nameOfVariable
    时,您不是在传递变量,而是在传递变量中的对象指针。如果该指针为
    nil
    ,则它与表示
    对象没有区别