NSFilePresenter — 这是一个Foundation协议,允许对象接收iOS和macOS文件系统中文件和目录更改的通知。类实现了协议的方法并通过NSFileCoordinator注册,之后系统会在对跟踪文件进行任何操作时自动调用这些方法。根据Apple Developer Documentation (2025),NSFilePresenter用于多线程访问文档的应用程序,以防止写入冲突。该协议必须与NSFileCoordinator一起使用——只有这样才确保安全的访问协调。
要点
NSFilePresenter — 这是一个Foundation协议,旨在跟踪Apple操作系统中的文件和目录更改。该协议定义了一组观察者对象实现的方法,以接收文件系统事件的通知。
该协议的主要任务是在多线程场景中确保对文件的安全访问。在iOS和macOS中,多个进程和线程可以通过NSFileCoordinator同时访问同一个文件,而NSFilePresenter确保每个参与者都获得最新的数据状态。
该协议从iOS 5.0和macOS 10.7开始包含在Foundation中。它用于处理文档、数据库和可能同时从不同来源更改的任何文件的应用程序——例如,通过iCloud同步或协作编辑时。
面向文档的应用程序 — NSFilePresenter的主要使用领域。使用UIDocument或NSDocument的应用程序通过NSFileCoordinator自动将自己注册为呈现者。这允许在处理从一个窗口或多个设备编辑同一文件时正确处理冲突。
iCloud同步 — 第二个关键场景。当文件在一个设备上更改时,iCloud将其同步到所有连接的设备。NSFilePresenter通知应用程序这些更改,允许及时更新界面。
多线程编辑器 — 第三个场景。在后台队列与用户工作同时加载和保存数据的应用程序中,NSFilePresenter防止在写入和读取文件时出现竞态条件。
工作机制 NSFilePresenter基于委托模式:对象实现协议方法,通过NSFileCoordinator注册,并在跟踪文件每次更改时接收调用。系统自行确定何时发生更改以及需要调用哪些方法。
该过程首先由对象创建NSFileCoordinator实例并通过传递文件URL调用协调器的方法。协调器检查是否为此URL注册了任何呈现者。如果有,它会阻止读取或写入访问,并通过协议方法通知呈现者即将进行的更改。
操作完成后,协调器解除锁定并调用最终通知。重要的是,呈现者不管理执行流程——它只对事件做出反应。协调完全由NSFileCoordinator负责。
准备阶段 — 在执行操作之前,协调器调用accommodatePresentedItemDeletion或accommodatePresentedSubitemDeletion。呈现者可以处理情况或通过返回错误取消操作。此阶段允许应用程序在文件更改之前正确地完成与文件的工作。
通知阶段 — 操作完成后,协调器调用presentedItemDidChange或presentedSubitemDidChange。呈现者收到文件已更改的信号,并可以重新读取其内容。对于文件移动,调用presentedItemDidMoveToURL并传递新位置。
完成阶段 — 协调器解除所有锁定并释放资源。呈现者可以继续使用更新后的数据。所有三个阶段在同一个线程中同步执行,因此协议方法必须快速执行,不进行长时间I/O操作。
NSFilePresenter协议包含几个必需和可选的方法。唯一的必需属性是presentedItemURL,返回跟踪文件或目录的URL。没有此属性,对象无法注册为呈现者。
presentedItemURL — 必须返回跟踪文件路径的URL?类型属性。如果对象跟踪多个文件,该属性返回主要元素的URL。对于目录,返回目录本身的URL。
presentedItemDidChange — 在跟踪文件内容更改后调用。在此方法中,呈现者更新其内部状态并重新加载数据。此方法不接收具体更改了哪些信息——只接收更改的事实。
accommodatePresentedItemDeletion — 在文件删除前调用。呈现者可以保存当前状态、关闭文件描述符或通过返回NSError取消操作。如果方法返回错误,则删除操作不会执行。
presentedItemDidMoveToURL — 在文件移动或重命名后调用。方法接收新的URL,呈现者必须更新对文件的引用。如果不实现此方法,呈现者将继续指向旧的、不存在的路径。
NSFileCoordinator和NSFilePresenter — 密不可分的一对。NSFileCoordinator管理对文件的访问并调用呈现者的方法。呈现者不直接与文件系统协作——所有操作都通过协调器进行,协调器保证更改的原子性。
协调器通过NSFileCoordinator类的addFilePresenter方法注册呈现者。添加后,呈现者开始接收通知。通过removeFilePresenter进行移除。系统持有对呈现者的弱引用,因此对象必须在整个跟踪期间保持活跃。
根据Apple WWDC 2022的信息,NSFileCoordinator使用内核级别的协调机制,这确保了锁定时的最小延迟。在最新版本的iOS中,协调器已优化为与Sandbox和应用程序扩展一起工作。
Intention — 每个读取或写入操作必须包装在协调块中:读取通过coordinateReadingItemAtURL,写入通过coordinateWritingItemAtURL。协调器在块执行期间自动锁定文件以防止其他参与者访问。
批量协调 — 对于涉及多个文件的操作,使用批量协调。协调器原子性地锁定所有指定文件,执行操作并解除锁定。这在移动或复制文档集时至关重要。
创建一个实现NSFilePresenter协议并跟踪文档文件更改的DocumentPresenter类。该类包含对文件的引用、内部数据和更新标志。
import Foundation
class DocumentPresenter: NSObject, NSFilePresenter {
var presentedItemURL: URL? {
return self.fileURL
}
var presentedItemOperationQueue: OperationQueue {
return self.queue
}
private let fileURL: URL
private let queue = OperationQueue()
func presentedItemDidChange() {
self.reloadData()
}
func accommodatePresentedItemDeletion() throws {
try self.saveCurrentState()
}
private func reloadData() {
let coordinator = NSFileCoordinator(filePresenter: self)
var error: NSError?
coordinator.coordinate(readingItemAt: self.fileURL,
options: [],
error: &error)
{ readURL in
guard let data = try? Data(contentsOf: readURL)
else { return }
self.processData(data)
}
}
private func processData(_: Data) {
// 文档数据处理
}
}
该类实现presentedItemDidChange以在文件更改时重新加载数据,以及accommodatePresentedItemDeletion以在删除之前保存状态。操作队列保证所有通知被顺序处理。
呈现者在打开文档时通过NSFileCoordinator.addFilePresenter注册。重要的是向协调器传递正确的读取选项——对于不修改的操作使用withoutChanges,对于需要立即访问的场景使用immediatelyAvailable。
第一个常见错误是缺少presentedItemOperationQueue的实现。如果不指定队列,通知可能来自任意线程,导致数据争用。始终使用串行OperationQueue来处理通知。
第二个错误是在呈现者方法中阻塞。协议方法从协调器同步调用。如果呈现者执行长时间操作(写入数据库、网络请求),它会阻塞协调器,影响所有其他参与者。将繁重操作移到后台队列。
第三个错误是忽略accommodatePresentedItemDeletion。如果呈现者不实现此方法且不返回错误,文件可能在未保存当前状态的情况下被删除。始终在此方法中保存数据(如果尚未写入磁盘)。
第四个错误是循环协调。当呈现者在通知方法内部再次为同一文件调用协调器时,会导致死锁。在处理程序内部启动协调之前检查isCoordinatedOperation标志。
| 错误 | 后果 | 解决方案 |
|---|---|---|
| 没有操作队列 | 多线程中的数据争用 | 指定OperationQueue |
| 方法中阻塞 | 协调器挂起 | 移到后台线程 |
| 忽略删除 | 删除时数据丢失 | 实现保存 |
| 循环协调 | 应用程序死锁 | isCoordinatedOperation标志 |
常见问题
NSFileHandle — 是用于读写数据的低级接口,不提供来自其他进程的更改通知机制。NSFilePresenter在协调级别工作:它从系统接收文件任何更改的事件,无论来源如何——另一个线程、进程或iCloud。
是的。没有NSFileCoordinator,NSFilePresenter就没有意义。呈现者只定义处理方法,而协调器管理锁定并调用这些方法。如果您在没有协调器的情况下使用NSFilePresenter,通知将不会被传递。
可以,但有局限性。presentedItemURL属性只返回一个URL,因此要跟踪多个文件,使用带有子元素附加方法的NSFilePresenter协议。替代方案是为每个文件创建单独的呈现者实例。
NSFilePresenter与iOS沙盒完全兼容。应用程序只能跟踪其容器内的文件。要访问其他应用程序的文件,使用App Groups或Security-Scoped Bookmark。协调器在沙盒权限范围内工作。
在presentedItemDidChange方法内部使用防抖或节流。创建一个延迟0.3-0.5秒的计时器,并在每次新调用时重置它。稳定后执行数据重新加载。这可以防止对同一变更包进行多次处理。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。