1

在我的 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
4

2 回答 2

2

NSNotifications 可以按名称和实例过滤。要么为每个通知选择不同的名称,要么为每个观察者注册它想要观察的特定对象实例。选择器只是告诉通知中心在决定观察者需要特定通知后调用什么方法。

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

于 2010-04-17T20:09:39.290 回答
1

我在对drawonward的回答的评论中所说的话是正确的。

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

除非您初始化局部变量,否则不会将其初始化为任何内容。所以,不要在没有分配给它或事先初始化它的情况下引用局部变量。随机的坏事会随机发生。

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

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

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

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

因此,您需要创建窗口并分配给变量 in init然后将自己添加为通知的观察者。

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

或者,更好的是,不用在代码中完成所有这些,只需使用 IB 来制作窗口。您需要制作testWindow一个插座并开始观察awakeFromNib

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

总之:

  1. 在发送该消息之前,创建窗口并将其指针分配给 in 中的变量testWindowinitaddObserver:selector:name:object:
  2. 对于用户可以显示和隐藏的窗口,将窗口的生命周期与其显示/隐藏状态分开。有一个有序的窗口对象是可以的;您无需在订购后立即销毁它。内存不再那么稀缺了——反正在 Mac 上也没有。仅当您真正完成它时才释放窗口 - 可能只是在dealloc.

(哦,还有一个样式/可维护性问题:不要像@"TestNotification"在代码中到处撒上文字字符串一样。在某处定义一个具有该值的变量,并在要使用通知的任何地方使用该变量。然后,要更改字符串,您在一个地方更改它,并且要重命名变量,您可以使用 Xcode 的 Refactor 工具。)

于 2010-04-19T06:49:21.927 回答