NSFilePresenter:这是什么,NSFileCoordinator协议和跟踪方法

作者: IT Sectr 发布日期: 2026-07-12 阅读时间: 7 分钟

NSFilePresenter — 这是一个Foundation协议,允许对象接收iOS和macOS文件系统中文件和目录更改的通知。类实现了协议的方法并通过NSFileCoordinator注册,之后系统会在对跟踪文件进行任何操作时自动调用这些方法。根据Apple Developer Documentation (2025),NSFilePresenter用于多线程访问文档的应用程序,以防止写入冲突。该协议必须与NSFileCoordinator一起使用——只有这样才确保安全的访问协调。

要点

  • NSFilePresenter — 用于跟踪iOS和macOS中文件和目录更改的Foundation协议。
  • NSFileCoordinator — 管理访问并调用委托方法的必需配对类。
  • accommodatePresentedItemDeletion — 用于处理跟踪文件删除并可取消的方法。
  • presentedItemDidChange — 在文件或目录内容发生任何更改时调用。
  • presentedItemURL — 返回跟踪文件URL的必需属性。

什么是NSFilePresenter?

NSFilePresenter — 这是一个Foundation协议,旨在跟踪Apple操作系统中的文件和目录更改。该协议定义了一组观察者对象实现的方法,以接收文件系统事件的通知。

该协议的主要任务是在多线程场景中确保对文件的安全访问。在iOS和macOS中,多个进程和线程可以通过NSFileCoordinator同时访问同一个文件,而NSFilePresenter确保每个参与者都获得最新的数据状态。

该协议从iOS 5.0和macOS 10.7开始包含在Foundation中。它用于处理文档、数据库和可能同时从不同来源更改的任何文件的应用程序——例如,通过iCloud同步或协作编辑时。

NSFilePresenter的应用场景

面向文档的应用程序 — NSFilePresenter的主要使用领域。使用UIDocument或NSDocument的应用程序通过NSFileCoordinator自动将自己注册为呈现者。这允许在处理从一个窗口或多个设备编辑同一文件时正确处理冲突。

iCloud同步 — 第二个关键场景。当文件在一个设备上更改时,iCloud将其同步到所有连接的设备。NSFilePresenter通知应用程序这些更改,允许及时更新界面。

多线程编辑器 — 第三个场景。在后台队列与用户工作同时加载和保存数据的应用程序中,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,呈现者必须更新对文件的引用。如果不实现此方法,呈现者将继续指向旧的、不存在的路径。

NSFilePresenter和NSFileCoordinator

NSFileCoordinatorNSFilePresenter — 密不可分的一对。NSFileCoordinator管理对文件的访问并调用呈现者的方法。呈现者不直接与文件系统协作——所有操作都通过协调器进行,协调器保证更改的原子性。

协调器通过NSFileCoordinator类的addFilePresenter方法注册呈现者。添加后,呈现者开始接收通知。通过removeFilePresenter进行移除。系统持有对呈现者的弱引用,因此对象必须在整个跟踪期间保持活跃。

根据Apple WWDC 2022的信息,NSFileCoordinator使用内核级别的协调机制,这确保了锁定时的最小延迟。在最新版本的iOS中,协调器已优化为与Sandbox和应用程序扩展一起工作。

协调规则

Intention — 每个读取或写入操作必须包装在协调块中:读取通过coordinateReadingItemAtURL,写入通过coordinateWritingItemAtURL。协调器在块执行期间自动锁定文件以防止其他参与者访问。

批量协调 — 对于涉及多个文件的操作,使用批量协调。协调器原子性地锁定所有指定文件,执行操作并解除锁定。这在移动或复制文档集时至关重要。

NSFilePresenter实现示例

创建一个实现NSFilePresenter协议并跟踪文档文件更改的DocumentPresenter类。该类包含对文件的引用、内部数据和更新标志。

swift
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?

NSFileHandle — 是用于读写数据的低级接口,不提供来自其他进程的更改通知机制。NSFilePresenter在协调级别工作:它从系统接收文件任何更改的事件,无论来源如何——另一个线程、进程或iCloud。

是否必须将NSFileCoordinator与NSFilePresenter一起使用?

是的。没有NSFileCoordinator,NSFilePresenter就没有意义。呈现者只定义处理方法,而协调器管理锁定并调用这些方法。如果您在没有协调器的情况下使用NSFilePresenter,通知将不会被传递。

一个对象可以是多个文件的呈现者吗?

可以,但有局限性。presentedItemURL属性只返回一个URL,因此要跟踪多个文件,使用带有子元素附加方法的NSFilePresenter协议。替代方案是为每个文件创建单独的呈现者实例。

NSFilePresenter如何与iOS中的Sandbox配合使用?

NSFilePresenter与iOS沙盒完全兼容。应用程序只能跟踪其容器内的文件。要访问其他应用程序的文件,使用App Groups或Security-Scoped Bookmark。协调器在沙盒权限范围内工作。

如果presentedItemDidChange调用太频繁怎么办?

在presentedItemDidChange方法内部使用防抖或节流。创建一个延迟0.3-0.5秒的计时器,并在每次新调用时重置它。稳定后执行数据重新加载。这可以防止对同一变更包进行多次处理。

总结

  • NSFilePresenter — 用于接收iOS和macOS中文件更改通知的Foundation协议,仅与NSFileCoordinator配合使用。
  • 必需属性 presentedItemURL — 没有它,对象无法注册为呈现者,也不会收到通知。
  • 主要方法 presentedItemDidChange在文件内容任何更改后调用——用于重新加载数据。
  • accommodatePresentedItemDeletion允许正确处理文件删除并保存应用程序的当前状态。
  • NSFileCoordinator管理锁定并保证操作的原子性——没有协调器,呈现者毫无用处。
  • 常见错误包括缺少操作队列、方法中阻塞和循环协调——通过设计避免这些非常重要。
  • 防抖presentedItemDidChange在频繁调用时——使用计时器在重新加载前将更改分组。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读