显示上下文NSMenu而不阻塞主线程



在显示上下文NSMenu时,我有一些意想不到的结果。我自己呈现菜单,而不是使用NSRespondermenu属性。结果表明,呈现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())发生在第二个线程上,而把菜单留在主线程上吗?

最新更新