自定义NSMenuItem子类触发动作时sender为NSMenuItem报错如何解决
问题根因
这个崩溃是Cocoa消息派发的类型匹配问题导致的:
AppKit中NSMenuItem的action方法默认传入的sender静态类型为NSMenuItem,你直接将方法参数声明为自定义子类FilerNSMenuItem时,Objective-C runtime仍按照父类实例的内存结构传入指针,方法内部直接访问子类新增的存储属性defaultFileExtension时,就会触发EXC_BAD_ACCESS内存错误,并非你创建的实例不是自定义子类类型。
修复方案
按照以下两步调整即可:
- 调整action方法的参数声明,不要直接指定为自定义子类类型,在方法内部做安全的类型转换后再访问自定义属性,这是Cocoa事件处理的标准写法,兼容所有绑定该action的菜单项:
@objc func SomeActions(_ sender: NSMenuItem) { // 安全转成自定义菜单项类型,转换失败直接兜底返回不崩溃 guard let filerItem = sender as? FilerNSMenuItem else { return } if filerItem.fileExtension != "" { // 正常处理逻辑 } }
- 纯代码创建菜单项时,明确指定变量的类型为自定义子类,避免编译器自动将变量类型推断为父类
NSMenuItem,导致实例属性初始化异常:
// 显式标注类型为FilerNSMenuItem let item: FilerNSMenuItem = FilerNSMenuItem(title: title, action: #selector(SomeActions(_:)), keyEquivalent: "") menuItem.submenu?.items.append(item)
注意:禁止使用强制解包转换(
as! FilerNSMenuItem),如果后续有其他普通NSMenuItem绑定了同一个action方法,会直接触发崩溃。如果是使用Xib/Storyboard创建的菜单项,需要在身份检查器中将菜单项的Class值修改为FilerNSMenuItem,否则运行时会创建默认NSMenuItem实例,无法转换成功。
内容的提问来源于stack exchange,提问作者Huang Runhua
相关产品推荐
相关产品推荐

