【发布时间】:2015-11-30 23:00:28
【问题描述】:
我正在维护一些旧代码,当它被提出以使用 10.8 SDK 而不是 10.7 SDK 进行编译时,某些菜单项的一些关键等效快捷方式停止工作。我的假设是这是因为这些菜单项处于禁用状态。不再工作的 NSMenuItems 是 autoenablesItems 已设置为 NO 的子菜单的一部分(我通过该子菜单的属性检查器在 xib 中确认了这一点,并通过查询 [NSMenu autoenablesItems] 以编程方式确认了这一点。它基本上出现对 [NSMenuItem setEnabled:] 的调用没有任何效果,因为如果我在调用 setEnabled:YES 后立即查询 [NSMenuItem isEnabled],则状态没有改变,它仍然为 isEnabled 返回 NO。这是一个 sn-p 代码它生成的输出:
printf("DEBUG: Current state of menu item is ");
[nsMenuItem isEnabled] ? printf("enabled\n") : printf("DISABLED!\n");
printf("DEBUG: Current state of menu autoenablesItems is ");
[nsMenu autoenablesItems] ? printf("YES\n") : printf("NO\n");
[nsMenuItem setEnabled:YES];
printf("DEBUG: Current state of menu after setting it is ");
[nsMenuItem isEnabled] ? printf("enabled\n") : printf("DISABLED!\n");
输出:
DEBUG: Current state of menu item is DISABLED!
DEBUG: Current state of menu autoenablesItems is NO
DEBUG: Current state of menu after setting it is DISABLED!
我还尝试继承 NSMenuItem 并覆盖 setEnabled 以查看是否有另一个调用 setEnabled 覆盖了我的调用,但没有其他调用通过 setEnabled。
如果我单击子菜单的父菜单,那么它似乎会更正状态并且这些 NSMenuItem 会启用,但它不会通过我的 setEnabled 代码来更改此状态。我尝试在启用时向子类添加一个观察者,以尝试在单击父菜单时捕获状态设置为启用的位置,但这也没有提供任何洞察力,因为观察者仅在我调用 setEnabled 时触发实际上并没有改变状态。
从阅读 Apple 文档中可以看出,只要父菜单上的 autoenablesItems 设置为 NO,NSMenuItem 的 setEnabled 应该可以工作,但在这种情况下似乎不起作用,我想不出出为什么。
如果我使用 10.7 SDK 编译,在相同的代码中(注意:这实际上是代码库的旧版本,所以也有其他差异,但这个特定的代码是相同的),我确实看到了调用 setEnabled 会更改 NSMenuItem 的状态。当它在 10.7 版本中输入此代码时,NSMenuItem 已经处于启用状态,但我确实尝试将调用更改为 setEnabled:NO 以确认这是否确实切换了 isEnabled 状态,并且确实如此,这与 10.8 版本不同.
任何想法为什么这在 10.8 中不起作用?我也尝试了 10.9,但它也没有在那里工作。我一直无法尝试 10.10 或 10.11,因为还有一些其他代码需要更新才能使用较新的 SDK 进行编译(同样,这是相当旧的遗留代码)。
【问题讨论】:
-
当这些日志被命中时,你确定 nsMenuItem 实际上不是 nil 吗?这将导致两个日志都打印 NO。另一种攻击途径是在任何相关的对象(目标、第一响应者)上定义菜单验证方法,并查看 (1) 它是否被调用,以及 (2) 它的返回值是否受到尊重,即使打开自动启用也是如此离开。发生了一些可疑的事情,因为这些东西是旧代码,并且在 Cocoa 中运行良好多年。侦探工作是需要的。
-
我已经确认 nsMenu 和 nsMenuItem 不是 nil。我还确认在单击顶级菜单项(nsMenu 是其子菜单)后,下一次调用代码时,nsMenu 和 nsMenuItem 仍然是相同的对象(调试器中的地址相同) ,然后 isEnabled 返回 true。 nsMenuItem 上的目标为 nil,但定义了一个操作。它在工作时也具有相同的状态。
-
当等效键起作用时,我们最终得到定义动作的代码(通过 NSApplication 的 sendEvent 后跟 NSObject 的 performSelector:withObject,然后是动作)。当等效键不起作用时,从 NSApplication 的 sendEvent 我们转到 NSWindow 的 sendEvent(首先通过子类的 sendEvent),然后是 NSWindow 的 _reallySendEvent:isDelayedEvent:,然后是视图的 keyDown:。所以这个动作永远不会被调用,我认为这是因为菜单项被禁用了。
-
用户界面中的菜单项实际上是否被禁用?显示为灰色文本等?我不禁认为 nsMenu 或 nsMenuItem 变量没有指向您认为的对象,因此您实际上是在操作错误的对象。
-
在 UI 中打开菜单会导致状态发生变化 - 单击菜单后,快捷方式起作用并且菜单项显示为已启用。这就像试图判断冰箱里的灯是否亮着一样——通过打开门,你正在改变你试图观察的状态。除了直观地查看菜单项是否被禁用之外,还有其他方法吗?我的猜测是它们被禁用了,因为键等效项不起作用,我的理解是,如果菜单项被禁用,键等效项将被禁用。我将尝试以编程方式更改标题以确认它是同一个项目。
标签: objective-c macos cocoa nsmenu nsmenuitem