Swift4+Xcode9.2下NSDocument无自动保存关闭最佳实践咨询
嘿,刚好我之前在Xcode 9+、Swift 4环境下做过NSDocument相关的开发,针对你说的无自动保存时关闭文档的场景,给你梳理下最佳实践,尤其是你提到的canClose(withDelegate:shouldClose:contextInfo:)这个核心方法的正确用法。
核心逻辑:关闭前的状态检查与用户交互
NSDocument的canClose方法是系统用来询问文档“是否可以被关闭”的关键入口——你调试看到的delegate和selector其实是系统内部的回调对象,我们不需要关心它的具体细节,只需要按照规则调用这个selector返回结果就行。
步骤1:重写canClose方法,处理未保存更改
这是实现的核心,下面是完整的Swift 4代码示例,完全适配你的开发环境:
import Cocoa class MyDocument: NSDocument { // 其他文档逻辑... override func canClose(withDelegate delegate: Any, shouldClose shouldCloseSelector: Selector, contextInfo: UnsafeMutableRawPointer?) { // 先判断文档有没有未保存的更改 guard self.hasUnsavedChanges else { // 没有未保存更改,直接告诉系统可以关闭 (delegate as? NSObjectProtocol)?.perform(shouldCloseSelector, with: true, with: contextInfo) return } // 有未保存更改,弹出系统风格的确认对话框 let closeAlert = NSAlert() closeAlert.messageText = "文档有未保存的更改" closeAlert.informativeText = "是否要保存更改后再关闭?" closeAlert.addButton(withTitle: "保存") closeAlert.addButton(withTitle: "不保存") closeAlert.addButton(withTitle: "取消") // 以sheet形式弹出(符合macOS交互规范) closeAlert.beginSheetModal(for: self.windowForSheet!) { [weak self] response in guard let self = self else { return } let responder = delegate as? NSObjectProtocol switch response { case .alertFirstButtonReturn: // 用户选择“保存” self.save(nil) { success, error in if success { // 保存成功,允许关闭 responder?.perform(shouldCloseSelector, with: true, with: contextInfo) } else { // 保存失败,弹出错误提示,拒绝关闭 if let saveError = error { let errorAlert = NSAlert(error: saveError) errorAlert.beginSheetModal(for: self.windowForSheet!) } responder?.perform(shouldCloseSelector, with: false, with: contextInfo) } } case .alertSecondButtonReturn: // 用户选择“不保存” // 手动标记文档为已保存,然后允许关闭 self.setHasUnsavedChanges(false) responder?.perform(shouldCloseSelector, with: true, with: contextInfo) case .alertThirdButtonReturn: // 用户选择“取消” // 拒绝关闭文档 responder?.perform(shouldCloseSelector, with: false, with: contextInfo) default: // 其他情况默认拒绝关闭 responder?.perform(shouldCloseSelector, with: false, with: contextInfo) } } } }
关键细节说明
- 必须调用selector:不管最终允许还是拒绝关闭,一定要调用
responder?.perform(shouldCloseSelector,...),否则系统会一直等待回调,导致界面卡住。 - 避免循环引用:在alert的闭包里用
[weak self],防止文档对象被意外持有无法释放。 - 适配新建文档场景:如果是从未保存过的新建文档,调用
save(nil)会自动弹出“另存为”对话框,不需要额外处理。 - 错误处理:保存失败时(比如磁盘满、权限不足),一定要给用户提示,同时拒绝关闭文档,避免数据丢失。
额外注意事项
- 如果你的文档有多个窗口,系统会在关闭最后一个窗口时才触发
canClose方法,这部分系统已经处理好了,不需要额外逻辑。 - 如果你需要自定义“未保存更改”的判断逻辑,可以重写
hasUnsavedChanges的getter方法,替换默认的检查逻辑。
这样实现完全符合macOS的人机交互规范,既能保证数据安全,又能给用户流畅的操作体验。
内容的提问来源于stack exchange,提问作者MyCometG3
相关产品推荐
相关产品推荐

