为什么我的应用程序无法"取消隐藏"(显示)

Lyn*_*son 8 macos cocoa objective-c

我正在调查Mac OS X 10.8上的一个问题,我的智慧结束了.我不知道下一步该做什么.

该应用程序是32位,并有一些碳调用.

问题出现了:当我右键单击Dock中的应用程序图标时,选择菜单项"Hide",然后,在应用程序隐藏后,我从Dock中选择"Show"菜单项,出现问题:主文档窗口没有出现(调色板和菜单确实出现).

此时,即使调色板变得可见,"显示"菜单项也不会更改为"隐藏".

当我从应用程序停靠菜单中选择"显示"时,我希望主文档窗口变得可见.就像其他Mac应用程序一样.

当它失败时,如果我使用触控板上的AppExposé手势显示文档窗口并选择主文档窗口,我可以再次显示主文档窗口.

如果我从终端或Xcode启动应用程序,它工作正常.文档窗口显示,我的应用程序的停靠菜单项按预期更改为"隐藏".我通过导航到*.app的父目录并输入来从终端启动应用程序./MyApp.app/Contents/MacOS/MyApp.

通过双击Finder中的应用程序图标启动它时失败.

当应用程序从终端和Xcode启动时,将显示来自应用程序委托的取消隐藏功能的日志消息,但在从Finder启动时则不会出现.

– applicationWillUnhide:
– applicationDidUnhide:
Run Code Online (Sandbox Code Playgroud)

我已经在Console.app中查找了抛出的任何异常(或任何其他消息).没有了.


更新:

为了尝试调试这个,我从Finder启动,并使用Xcode附加到进程.

我怀疑是抛出异常,当我使用Xcode的"Exception Breakpoint"测试它以及在objc_exception_throw(以防万一)上放置一个断点时,当我隐藏或"显示"应用程序时它不会中断.

然后我想我需要证明那个NSApplicationWillUnhideNotification并且NSApplicationDidUnhideNotification被送出去了.它们是我从Xcode或终端启动的,但是如果我从Finder启动,它们就不是.

在将Xcode附加到应用程序之后,通过"添加符号断点"为以下内容添加断点,我验证了这一点:

-[NSNotificationCenter postNotificationName:object:userInfo] 
Run Code Online (Sandbox Code Playgroud)

然后,我添加了一个调试器命令:"po*(id*)($ esp + 12)"以打印出该选择器的第一个参数(通知名称).

这是我在StackOverflow中发布答案中找到的.

使用它,我可以看到在选择"显示"菜单项后发布的通知.当我从Xcode/Terminal启动时,我看到发布了以下通知:

NSApplicationWillUpdateNotification, NSWindowDidUpdateNotification, NSApplicationDidUpdateNotification, ** NSApplicationWillUnhideNotification **, ..., ** NSApplicationDidUnhideNotification **, ..., NSApplicationWillBecomeActiveNotification, ...
Run Code Online (Sandbox Code Playgroud)

NSApplicationWillUnhideNotification 是在这种情况下发布的.

当我从Finder启动时,我看到发布了以下通知:

NSApplicationWillUpdateNotification, NSWindowDidUpdateNotification, NSApplicationDidUpdateNotification, NSApplicationWillBecomeActiveNotification, ...
Run Code Online (Sandbox Code Playgroud)

它没有发送NSApplicationWillUnhideNotification.此外,当我从Xcode推出的版本中选择"显示"时,我会-[NSApplication _doUnhideWithoutActivation]在回溯中看到.当我附加到Finder启动的版本时,为该函数设置断点不会导致我选择"显示"时中断.

然后,我想,也许应用程序认为它没有被隐藏.

我有一个空闲事件处理程序,所以从那里我打印出[[NSApplication sharedApplicaton] isHidden]隐藏和"显示"应用程序时的值.

针对该问题的情况下,不隐藏应用程序时,它打印出NOisHidden.当应用程序被隐藏,它打印出YESisHidden.当我从停靠栏菜单中选择"显示",它继续打印出来NOisHidden.它知道它是隐藏的,但是应用程序的一部分已被激活:NSPanelsNSMenuBar出现.

我可以通过进入应用程序Exposé模式看到文档窗口,单击文档窗口将显示窗口,但停靠菜单项仍然是"显示"并且isHidden仍然是YES.

取消隐藏机制适用于示例应用程序,所以我很确定我们的代码正在做一些事情来关闭它.


我想知道从终端启动的应用程序与从Finder启动的应用程序之间会有什么不同?

我让应用程序记录了环境变量使用[[NSProcessInfo processInfo] environment],我唯一能看到的真正区别是PWD存在于终端应用程序的变量中:我在代码中看不到任何使用它的东西.

我让应用程序通过命令行参数登录[[NSProcessInfo processInfo] arguments],我确实在Finder启动的版本中看到了不同的东西.终端和Finder启动版本都将二进制路径列为第一个参数; Finder还列出了第二个参数"-psn_0_89445704".我在网上看到,这是Mac OS X添加到GUI应用程序的命令行参数的东西,我看到它添加到其他应用程序的命令行参数,从Dock菜单中正确隐藏和显示.

你还有其他想法可能会让我进一步解决这个谜团吗?感谢您的任何帮助或建议!

Lyn*_*son 3

经过与Apple工程师在AppKit方面的合作,找到了解决方案。

在我们的应用程序中,我们通过此方法出于各种原因“刷新”事件队列:

NSEvent* lastEvent = [NSEvent otherEventWithType:NSPeriodic
                                            location:NSMakePoint(0.0, 0.0)
                                            modifierFlags:0
                                           timestamp:[NSDate timeIntervalSinceReferenceDate]
                                        windowNumber:1
                                             context:NULL
                                             subtype:0
                                               data1:0
                                               data2:0];

    [[NSApplication sharedApplication] discardEventsMatchingMask:NSAnyEventMask beforeEvent:lastEvent];
Run Code Online (Sandbox Code Playgroud)

Mac OS X 系统在启动时向应用程序发送“Show”事件。我们的刷新函数在启动时调用,有效地从队列中删除该事件,但 Mac OS X 的核心进程部分有自己的内部队列,用于跟踪显示和隐藏以及其他类型的事件类型,以便它不会不要发送重复的消息。(我将调查是否真的有必要冲洗)

问题是,当对每个discardEventsMatchingMask:NSAnyEventMask事件调用时,它会清除应用程序的事件,但不会响应核心进程的显示事件,因此核心进程认为不需要再次发送显示事件。

这个特定问题的解决方案是更有选择性地清除事件。在我的新实现中,我没有清除核心流程将发送的事件。

/* a bug in Apple's Core Process group forces me to isolate which events should be cleared as
        show|hide|activate|deactivate messages get sent by Core Process, but are not _marked_ as
     handled and so Core Process thinks that the "Show" event is still pending and will not send
     another */

    NSEvent* lastEvent = [NSEvent otherEventWithType:NSPeriodic
                                            location:NSMakePoint(0.0, 0.0)
                                            modifierFlags:0
                                           timestamp:[NSDate timeIntervalSinceReferenceDate]
                                        windowNumber:1
                                             context:NULL
                                             subtype:0
                                               data1:0
                                               data2:0];


    NSEventMask maskForEventsToDiscard = (NSPeriodic |
                                          NSLeftMouseDown |
                                          NSLeftMouseUp |
                                          NSMouseMoved |
                                          NSLeftMouseDragged |
                                          NSRightMouseDragged |
                                          NSMouseEntered |
                                          NSMouseExited |
                                          NSKeyDown |
                                          NSOtherMouseDown |
                                          NSOtherMouseUp |
                                          NSOtherMouseDragged);

    [[NSApplication sharedApplication] discardEventsMatchingMask:maskForEventsToDiscard
                                                     beforeEvent:lastEvent];
Run Code Online (Sandbox Code Playgroud)

由于“显示”事件在启动时未清除,因此现在可以显示和隐藏工作!

特别感谢Apple的KF!