在显示上下文NSMenu
时,我有一些意想不到的结果。我自己呈现菜单,而不是使用NSResponder
的menu
属性。结果表明,呈现NSMenu
是一个阻塞操作。
在一个新的模板项目中,我只在applicationDidLaunch:
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Menu"];
[menu addItemWithTitle:@"Item 1" action:@selector(selected:) keyEquivalent:@""];
[menu addItemWithTitle:@"Item 2" action:@selector(selected:) keyEquivalent:@""];
[menu addItemWithTitle:@"Item 3" action:@selector(selected:) keyEquivalent:@""];
[menu popUpMenuPositioningItem:[menu itemAtIndex:0]
atLocation:CGPointMake(500, 500)
inView:nil];
NSLog(@"Context menu shown.");
popUpMenuPositioningItem:atLocation:inView:
调用不会返回,直到我选择一个项目或单击外部自动取消菜单。日志只在菜单关闭时打印。
为什么会发生这种情况?我该如何预防呢?我发现有几次提到这个问题,但"解决方案"总是包括在后台执行其他事情,而不是防止NSMenu
阻塞。
如果我在后台线程中呈现菜单,菜单会变得无响应,不会对内部或外部的点击做出反应。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[[menu popUpMenuPositioningItem:[menu itemAtIndex:0]
atLocation:CGPointMake(500, 500)
inView:nil];
});
我不认为你所要求的是可能的,但是,有一些事情你可以做,看起来很相似:
1)使用NSPopover代替菜单。这是异步的,旨在在许多可以使用弹出式菜单的上下文中使用。如果你需要支持没有弹出窗口的操作系统版本,你也可以创建一个带有按钮的NSWindow。
2)如果你正在尝试运行计时器,或其他在运行循环上被调度的操作(例如下载),你可以在nseventrackingrunloopmode中调度这些。默认情况下,它们只被安排在nsdefaulunloopmode(这是有道理的,因为你不想在计时器触发并显示警报时取消用户的菜单选择)。
老实说,你想做的事情似乎有点毫无意义,令人困惑,倒退和滥用菜单。但那可能只是因为我不知道你想达到什么目的。你的高级任务是什么?当菜单启动时,主线程上需要发生什么?难道你不能让其他的事情(在你的例子中NSLog())发生在第二个线程上,而把菜单留在主线程上吗?