You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:32:04