这里的问题是,即使在菜单跟踪模式下,您也需要触发回调。
例如,-[NSTask waitUntilExit]“使用 NSDefaultRunLoopMode 轮询当前运行循环,直到任务完成”。这意味着在菜单关闭之前它不会运行。此时,调度 updateTheMenu 以在 NSCommonRunLoopMode 上运行并没有帮助——毕竟它无法及时返回。我相信 NSNotificationCenter 观察者也只在 NSDefaultRunLoopMode 中触发。
如果您能找到某种方法来安排即使在菜单跟踪模式下也能运行的回调,那么您就可以了;您可以直接从该回调中调用 updateTheMenu。
- (void)updateTheMenu {
static BOOL flip = NO;
NSMenu *filemenu = [[[NSApp mainMenu] itemAtIndex:1] submenu];
if (flip) {
[filemenu removeItemAtIndex:[filemenu numberOfItems] - 1];
} else {
[filemenu addItemWithTitle:@"Now you see me" action:nil keyEquivalent:@""];
}
flip = !flip;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSTimer *timer = [NSTimer timerWithTimeInterval:0.5
target:self
selector:@selector(updateTheMenu)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
运行这个并按住文件菜单,你会看到额外的菜单项每半秒出现和消失一次。显然“每半秒”不是你要找的,而且 NSTimer 不明白“我的后台任务何时完成”。但是可能有一些同样简单的机制可以使用。
如果没有,您可以使用 NSPort 子类之一自己构建它——例如,创建一个 NSMessagePort 并在完成后让您的 NSTask 写入它。
The only case you're really going to need to explicitly schedule updateTheMenu the way Rob Keniger described above is if you're trying to call it from outside of the run loop. For example, you could spawn a thread that fires off a child process and calls waitpid (which blocks until the process is done), then that thread would have to call performSelector:target:argument:order:modes: instead of calling updateTheMenu directly.