被移除的通知

Posted

技术标签:

【中文标题】被移除的通知【英文标题】:Notifications being removed 【发布时间】:2018-08-25 18:20:44 【问题描述】:

我有一个模型对象和一个窗口控制器。我希望他们能够通过通知进行交流。我在 App Delegate 的 -applicationDidFinishLaunching: 方法中创建了这两个。我在加载窗口控制器的窗口后添加观察者,如下所示:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
    WordSetWindowController* windowController = [[WordSetWindowController alloc] initWithWindowNibName:@"WordSetWindowController"];

    model = [[WordSetModel alloc] init];

    NSWindow*   window = windowController.window;

    [[NSNotificationCenter defaultCenter] addObserver:windowController
                                             selector:@selector(handleNotification:)
                                                 name:kNotification_GeneratingPairs
                                               object:model];

    [[NSNotificationCenter defaultCenter] addObserver:windowController
                                             selector:@selector(handleNotification:)
                                                 name:kNotification_ProcessingPairs
                                               object:model];

    [[NSNotificationCenter defaultCenter] addObserver:windowController
                                             selector:@selector(handleNotification:)
                                                 name:kNotification_UpdatePairs
                                               object:model];

    [[NSNotificationCenter defaultCenter] addObserver:windowController
                                             selector:@selector(handleNotification:)
                                                 name:kNotification_Complete
                                               object:model];

    [model initiateSearch];

-iniateSearch 方法启动一些线程以在后台执行一些处理器密集型计算。我希望这些线程发送通知,以便在处理过程中更新 UI。它看起来像这样:

- (void)initiateSearch;

    [[NSNotificationCenter defaultCenter] postNotificationName:kNotification_GeneratingPairs
                                                        object:self];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
        // ... do the first part of the calculations ...

        // Notify the UI to update
        self->state = SearchState_ProcessingPairs;
        dispatch_async(dispatch_get_main_queue(), ^
            [[NSNotificationCenter defaultCenter] postNotificationName:kNotification_ProcessingPairs
                                                                object:self];
        );

        // ... Do some more calculations ...

        // Notify the UI that we're done
        self->state = SearchState_Idle;
        dispatch_sync(dispatch_get_main_queue(), ^
            [[NSNotificationCenter defaultCenter] postNotificationName:kNotification_Complete
                                                                object:self];
        );

    );

第一个通知正常工作,但dispatch_async() 调用中发生的所有通知都不会导致调用通知处理程序。我尝试在后台线程和 UI 线程上调用-postNotificationName::(都使用dispatch_async(dispatch_get_main_queue(),...) 和调用-performSelectorOnMainThread:::),但都没有任何效果。

很好奇,我通过NSTimer 添加了一个调用,该调用在-initiateSearch: 结束时的大dispatch_async() 调用后等待5 秒,发现即使这一切都发生在主UI 线程上,它也没有触发通知处理程序。如果我只是在dispatch_async() 调用返回后立即调用postNotification:::,它可以正常工作。

据此,我得出的结论是,尽管我的代码从未调用 -removeObserver:,但观察者以某种方式从通知中心中移除。为什么会发生这种情况,我怎样才能避免这种情况发生,或者在哪里可以将我的电话转移到 -addObserver 以便他们不受此影响?

【问题讨论】:

kNotification_GeneratingPairs 通知是否已触发?您在handleNotification: 函数中收到它? 是的,它会触发并调用handleNotification: 函数。如果我将它移动到dispatch_async() 中的第一行,它不会触发,即使它被重新调度到主 UI 线程。似乎关于dispatch_async() 的某些东西正在导致观察者被移除。但是,如果我将它移到 dispatch_async() 调用返回之后,它就会触发,除非它在计时器上并等待几秒钟。 【参考方案1】:

怀疑您的窗口控制器正在被释放。

您将它分配给WordSetWindowController* windowController,但该引用在-applicationDidFinishLaunching: 的末尾消失了,并且可能是您的窗口控制器。

由于窗口本身在打开时由 AppKit 保留,因此您最终会得到一个没有控制器的屏幕窗口。 (NSWindowNSNotificationCenter 都没有对其控制器/观察者保持强引用。)

初始通知有效,因为这些通知是在 -applicationDidFinishLaunching: 结束之前发布和/或该事件的自动释放池已耗尽。

在应用程序委托中创建对窗口控制器的强引用,将窗口控制器的引用存储在那里,我怀疑一切都会像宣传的那样工作。

在我身上发生了非常相似的事情,它也是一个表委托的窗口控制器;初始设置和委托消息可以完美运行,但后来选择事件神秘地消失了。

【讨论】:

是的,ARC 是你的朋友……直到不是,哈哈

以上是关于被移除的通知的主要内容,如果未能解决你的问题,请参考以下文章

新版Sublime text3注册码被移除的解决办法

不用自动移除的通知中心

QVBoxLayout移除控件之后没有消失

怎样移除掉所有Fragment

通知观察者多次调用,即使观察者被移除

数组操作的4种小方法