NSNotification对多个对象

时间:2010-04-17 20:03:02

标签: cocoa nsnotifications

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

2 个答案:

答案 0 :(得分:2)

可以按名称和实例过滤NSNotifications。为每个通知选择不同的名称,或者为每个观察者注册它想要观察的特定对象实例。选择器只是告诉通知中心一旦确定观察者想要特定通知就调用哪种方法。

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

答案 1 :(得分:1)

我对我在评论中所说的内容是正确的。

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

除非您初始化局部变量,否则局部变量不会初始化为任何内容。因此,不要永远引用局部变量而不分配它或先前初始化它。随机坏事会随机发生。

另一方面,实例变量初始化为bar,并且在您将其他内容添加到其中之前将继续包含nil。这很重要,因为nil完全没有在init实例变量中添加任何内容,因此它包含testWindow

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

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

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

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

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

或者,更好的是,不要在代码中执行所有操作,只需使用IB来创建窗口。您需要将init作为插座并开始在testWindow中观察。

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

总结:

  1. 创建窗口并将其指针指向nil中的testWindow变量,之前发送init消息。
  2. 对于用户可以显示和隐藏的窗口,请将窗口的生命周期与其显示/隐藏状态分开。有一个订购的窗口对象是可以的;一旦你订购它就不需要销毁它。记忆不再那么稀少 - 不管怎么说都不在Mac上。只有当你真正完成它时才释放窗口 - 可能只是在addObserver:selector:name:object:
  3. (哦,风格/维护性问题:不要在代码上撒上像dealloc这样的文字字符串。在某处定义一个带有该值的变量,并在任何想要使用通知的地方使用该变量。然后,要更改字符串,只需在一个地方更改它,并重命名变量,就可以使用Xcode的Refactor工具。)