ios - 监视文件更改时打开的文件过多
问题描述
我正在为 iPad 开发一个基于文档浏览器的应用程序。我一直在使用SKQueue来监视对文件的更改,以确保当用户在文档浏览器中执行操作时它们的元数据保持最新。启动监控的代码:
// Set up the queue
let delegate = self
queue = SKQueue(delegate: delegate)!
// Remove all existing paths
queue?.removeAllPaths()
// Get the list of PDF URLs using a function that enumerates a folder's contents
let pdfFiles = getFolderContents(rootFolder: myDocumentsFolder, extensionWanted: "pdf")
for pdfFilePath in pdfFiles.filePaths {
queue?.addPath(pdfFilePath.path)
}
for pdfFolderPath in pdfFiles.folderPaths {
queue?.addPath(pdfFolderPath.path)
}
我开发了自己的逻辑来响应来自此队列的通知,但在应用程序运行时我不会从队列中删除任何项目。
问题 - 似乎当观看的项目数量超过 200(文件和文件夹)时,系统会遇到问题,并且控制台会报告错误 24:打开的文件过多。之后,不能执行任何文件的读/写。
根据我从搜索中收集到的信息,iOS 和 iPadOS 似乎不允许同时访问超过 256 个文件描述符,这意味着 GCD 监控文件更改的方法将受到同样的限制.
有没有办法监控不受这种限制的文件更改?还有其他建议吗?
解决方案
经过大量的研究和实验,我终于可以验证,打开文件描述符的默认最大数量是 256 - 对于 MacOS、iOS 和 iPadOS。这可以在 MacOS 中轻松更改 - 请参阅此处的文章。然而,iOS 和 iPadOS 在本质上更加封闭,没有可靠的方法来改变这些平台上的这个限制。
因此,良好做法是:
- 尽量避免设计一个需要打开这么多文件描述符的应用程序。
- 监视目录,而不是单个文件。借助可用于监视文件系统的大多数工具,您可以在受监视目录中收到任何文件更改的通知。只需从那里通过枚举文件夹并将其新状态与保存的状态进行比较来实现您的逻辑。您可以在此SO 线程的前两个答案中找到很好的代码。
注意:我建议使用枚举而不是其他获取文件系统状态的方法,因为其他方法往往会在模拟器和实际设备之间给出不兼容的结果(对符号链接分辨率的不同处理)。
- 确保您选择的监视文件系统的方法可以查询正在监视的项目数,并在用户接近设定的限制时发出警报。请记住,256 个打开的文件还必须包括应用程序使用的所有文件,包括应用程序包中的文件和实际使用的文件。所以采取一个不错的安全边际。
就我而言,我的应用程序使用 UIDocumentBrowserViewController,或者换句话说 - Apple 自己的文件应用程序,以允许用户管理他们的文件。我必须使我的元数据与文件系统状态保持同步,而且我无法控制用户的文件管理习惯。更复杂的是,文件应用程序本身可用于修改应用程序的文件系统 - 而我的应用程序未处于活动状态。
因此,我做两件事:
- 我将来自 App Delegate 的 applicationDidEnterBackground 和 applicationWillTerminate 方法的文件系统的详细状态保存到 Application Support 中的 json 文件中,并在应用程序启动时将其与文件系统的新枚举进行比较 - 并提醒用户任何不匹配,建议下次使用应用自带的文件浏览器。
- 我创建了自己的 swift 包,名为SFSMonitor,用于监控文件系统。它基于极其方便的SKQueue(我强烈推荐),但它不是监控 kevent,而是使用 Dispatch Sources——Apple 提倡的更现代的方法。它类似于 Apple 自己的目录监视器(您可以在此处找到参考),但它不是监视一个目录,而是允许您创建和管理它们的整个队列。此类允许您设置受监视文件描述符的最大数量,并在达到该限制时收到通知。
推荐阅读
- clojure - wrap-cors 中间件不适用于 system.components
- java - 索引越界 Java
- python - 仅在 pandas groupby 中包含前 N 次出现的辅助列
- azure-active-directory - 如何使用 Azure AD 仅对具有不同子域但域与其他网站相同的网站之一实施 SSO?
- wordpress - WordPress:在我的搜索结果中的某些搜索页面上找不到带有 Relevanssi 的页面
- amazon-s3 - 如何忽略错误但不跳过 redshift 复制命令中的行
- php - 警告:为 foreach() 提供的参数无效,字体加载问题
- kotlin - 如何在 Kotlin 中初始化大的不可变映射?
- angular - 从 Angular 中的 Observables 返回数据
- javascript - 在 Jest 中模拟文件加载期间调用的函数