将NSFilePromiseProvider写入粘贴板导致Mac应用退出卡顿
问题解析:NSFilePromiseProvider写入粘贴板导致应用退出延迟
问题背景
在Mac应用中执行「编辑→复制」操作时,会将以kUTTypeFileURL初始化的NSFilePromiseProvider对象写入通用粘贴板,以此实现文件向Finder等应用复制。但退出应用时会出现严重延迟(含沙滩球卡顿),控制台日志显示存在粘贴板活动,系统似乎在确保文件承诺仍可被提供。
目前找到的所有示例均是在表格视图拖放场景中使用NSFilePromiseProvider,未发现直接将其写入粘贴板的案例(尽管它实现了NSPasteboardWriting协议)。
经测试发现:若自定义的NSFilePromiseProvider在writingOptions(forType:pasteboard:)方法中对所有类型返回[],应用即可无延迟关闭。现需解析:
- 问题最初产生的原因
- 此规避方案生效的原理
相关代码实现
自定义FilePromiseProvider
class FilePromiseProvider: NSFilePromiseProvider { struct UserInfoKeys { static let fileURL = "fileURLKey" static let row = "rowKey" } override func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] { var types = super.writableTypes(for: pasteboard) if let userInfoDict = userInfo as? [String: Any] { if userInfoDict[UserInfoKeys.row] != nil { types.append(.fileListTableRow) } if userInfoDict[UserInfoKeys.fileURL] != nil { types.append(.fileURL) types.append(.string) } } return types } override func writingOptions(forType type: NSPasteboard.PasteboardType, pasteboard: NSPasteboard) -> NSPasteboard.WritingOptions { // 返回[]时应用无延迟关闭,默认实现会返回.promise导致系统额外处理 return [] } override func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? { guard let userInfoDict = userInfo as? [String: Any] else { return nil } switch type { case .fileListTableRow: if let row = userInfoDict[UserInfoKeys.row] as? Int { return row } case .fileURL: if let url = userInfoDict[UserInfoKeys.fileURL] as? NSURL { return url.pasteboardPropertyList(forType: type) } case .string: if let url = userInfoDict[UserInfoKeys.fileURL] as? NSURL { return url.lastPathComponent } default: break } return super.pasteboardPropertyList(forType: type) } }
复制操作写入粘贴板代码
@IBAction func copy(_ sender: Any?) { guard let urls = representedObject as? [URL] else { return } let itemIndexes = tableView.selectedRowIndexes if itemIndexes.isEmpty { return } var filePromises = [FilePromiseProvider]() for idx in itemIndexes { var userInfo = [String: Any]() userInfo[FilePromiseProvider.UserInfoKeys.fileURL] = urls[idx] let filePromise = FilePromiseProvider(fileType: UTType.fileURL.identifier, delegate: self) filePromise.userInfo = userInfo filePromises.append(filePromise) } NSPasteboard.general.clearContents() NSPasteboard.general.writeObjects(filePromises) }
NSFilePromiseProviderDelegate实现
extension ViewController: NSFilePromiseProviderDelegate { func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, fileNameForType fileType: String) -> String { let fileURL = queryFileURL(from: filePromiseProvider) return fileURL?.lastPathComponent ?? "" } func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, writePromiseTo url: URL, completionHandler: @escaping (Error?) -> Void) { do { if let atURL = queryFileURL(from: filePromiseProvider) { try FileManager.default.copyItem(at: atURL, to: url) } completionHandler(nil) } catch let error { OperationQueue.main.addOperation { self.presentError(error, modalFor: self.view.window!, delegate: nil, didPresent: nil, contextInfo: nil) } completionHandler(error) } } func operationQueue(for filePromiseProvider: NSFilePromiseProvider) -> OperationQueue { return filePromiseQueue } private func queryFileURL(from filePromiseProvider: NSFilePromiseProvider) -> URL? { if let userInfo = filePromiseProvider.userInfo as? [String: Any] { return userInfo[FilePromiseProvider.UserInfoKeys.fileURL] as? URL } return nil } }
技术解析
1. 问题产生的原因
NSFilePromiseProvider的默认writingOptions(forType:pasteboard:)实现会对文件承诺相关类型返回.promise选项。当你将该对象写入粘贴板后:
- 系统会认定这些文件承诺需要由你的应用持续提供,直到接收方(如Finder)完成文件接收。
- 当应用退出时,系统会检查粘贴板上的活跃承诺:若存在标记为
.promise的项,系统会触发内部流程,要么等待应用完成承诺,要么将文件预先拷贝到系统临时目录,这个过程会阻塞应用退出,导致延迟和卡顿。 - 你在
writableTypes中额外添加了.fileURL和.string类型,默认实现会对这些类型也应用.promise逻辑,进一步增加了系统的处理开销。
2. 规避方案生效的原理
当你在writingOptions(forType:pasteboard:)中返回空数组[]时:
- 相当于告诉系统,所有粘贴板类型的内容都是即时提供的,而非承诺式的。系统无需再跟踪这些粘贴板项的提供者状态。
- 应用退出时,系统不会触发额外的承诺验证或文件备份流程,因此不会出现阻塞延迟。
- 你的
pasteboardPropertyList(forType:)方法已经为.fileURL和.string类型提供了直接的属性列表值,完全不需要依赖承诺机制,所以返回空选项不会影响文件复制功能的正常运行。
内容的提问来源于stack exchange,提问作者fhe
相关产品推荐
相关产品推荐

