iOS文件系统编程指南

iOS
4.2k 词

iOS文件系统编程指南

iOS文件系统编程指南

文件系统处理数据文件、应用程序以及与操作系统本身关联的文件的持久存储。因此,文件系统是所有进程使用的基本资源之一。

APFS 是 macOS、iOS、watchOS 和 tvOS 中的默认文件系统。APFS 取代 HFS+ 作为 iOS 10.3 及更高版本以及 macOS High Sierra 及更高版本的默认文件系统。

关于 iOS 文件系统

iOS 文件系统面向独立运行的应用程序。为了保持系统简单,iOS 设备的用户无法直接访问文件系统,应用应遵循此约定。

iOS 标准目录:文件所在的位置

目录 描述
AppName.app 这是应用程序的捆绑包。此目录包含应用程序及其所有资源。您不能写入此目录。为防止篡改,捆绑目录在安装时进行了签名。写入此目录会更改签名并阻止您的应用程序启动。但是,您可以获得对存储在应用程序包中的任何资源的只读访问权限。此目录的内容未由 iTunes 或 iCloud 备份。但是,iTunes 会对从 App Store 购买的任何应用程序执行初始同步。
Documents/ 使用此目录存储用户生成的内容。该目录的内容可以通过文件共享提供给用户;因此,该目录应该只包含您可能希望向用户公开的文件。此目录的内容由 iTunes 和 iCloud 备份。
Documents/Inbox 使用此目录可访问外部实体要求您的应用程序打开的文件。具体来说,Mail 程序将与您的应用相关联的电子邮件附件放在该目录中。文档交互控制器也可以在其中放置文件。您的应用程序可以读取和删除此目录中的文件,但不能创建新文件或写入现有文件。如果用户试图编辑此目录中的文件,您的应用程序必须在进行任何更改之前静默将其移出目录。此目录的内容由 iTunes 和 iCloud 备份。
Library/ 这是所有非用户数据文件的顶级目录。您通常将文件放在几个标准子目录之一中。iOS 应用通常使用Application SupportCaches子目录;但是,您可以创建自定义子目录。将Library子目录用于您不想向用户公开的任何文件。您的应用不应将这些目录用于用户数据文件。目录的内容Library(子目录除外Caches)由 iTunes 和 iCloud 备份。
tmp/ 使用此目录写入不需要在应用程序启动之间保留的临时文件。当不再需要时,您的应用程序应从该目录中删除文件;但是,当您的应用程序未运行时,系统可能会清除此目录。此目录的内容未由 iTunes 或 iCloud 备份。

APP 文件应该存放的位置

为防止 iOS 设备上的同步和备份过程花费很长时间,请选择放置文件的位置。

存储大文件的应用程序会减慢备份到 iTunes 或 iCloud 的过程。

这些应用程序还会占用用户的大量可用存储空间,这可能会促使用户删除该应用程序或禁止将该应用程序的数据备份到 iCloud。

考虑到这一点,您应该根据以下准则存储应用程序数据:

  • 将用户数据放入Documents/. 用户数据通常包括您可能希望向用户公开的任何文件——您可能希望用户创建、导入、删除或编辑的任何文件。对于绘图应用程序,用户数据包括用户可能创建的任何图形文件。对于文本编辑器,它包括文本文件。视频和音频应用程序甚至可能包含用户下载以供稍后观看或收听的文件。
  • 将应用程序创建的支持文件放在该Library/Application support/目录中。通常,此目录包含应用程序用来运行但应该对用户隐藏的文件。此目录还可以包含数据文件、配置文件、模板和从应用程序包加载的资源的修改版本。
  • 请记住,默认情况下会备份Documents/和中的文件。您可以通过使用键Application Support/调用从备份中排除文件。任何可以重新创建或下载的文件都必须从备份中排除。这对于大型媒体文件尤为重要。如果您的应用程序下载视频或音频文件,请确保它们不包含在备份中。 -[NSURL setResourceValue:forKey:error:]``NSURLIsExcludedFromBackupKey
  • 将临时数据放在tmp/目录中。临时数据包括您不需要长时间保留的任何数据。请记住在完成这些文件后将其删除,以免它们继续占用用户设备上的空间。当您的应用程序未运行时,系统会定期清除这些文件;因此,您不能指望这些文件在您的应用程序终止后仍然存在。
  • 将数据缓存文件放在Library/Caches/目录中。缓存数据可用于需要比临时数据保留更长时间但不如支持文件那么长的任何数据。一般而言,应用程序不需要缓存数据即可正常运行,但可以使用缓存数据来提高性能。缓存数据的示例包括(但不限于)数据库缓存文件和暂时的可下载内容。请注意,系统可能会删除该Caches/目录以释放磁盘空间,因此您的应用必须能够根据需要重新创建或下载这些文件。

iCloud 文件储存容器

iCloud 为使用 iCloud 的应用程序提供了一个结构化的文件存储系统:

  • 应用程序有一个主要的 iCloud 容器目录,用于存储它们的本机文件。他们还可以访问在其应用程序授权中列出的辅助 iCloud 容器目录。
  • 在每个容器目录中,文件被分为“文档”和数据。位于Documents子目录(或其子目录之一)中的每个文件或文件包都作为可以单独删除的单独文档呈现给用户(通过 macOS 和 iOS 中的 iCloud UI)。任何不在Documents其子目录中或其子目录之一的内容都被视为数据,并在 iCloud UI 中显示为单个条目。

用户在应用程序的用户界面中创建和查看的文档(例如 Pages、Numbers 和 Keynote 中的文档浏览器)应存储在该Documents目录中。

另一个可能进入Documents目录的文件示例是保存的游戏,同样是因为它们是应用程序可能提供某种选择方法的东西。

应用程序不希望用户直接查看或修改的任何内容都应放在Documents目录之外。应用程序可以在容器目录中创建任何子目录,因此它们可以根据需要排列私有文件。

应用程序在 iCloud 容器目录中创建文件和目录的方式与它们创建本地文件和目录的方式完全相同。并且所有文件的属性都被保存,如果他们向文件添加扩展属性,这些属性也会被复制到 iCloud 和用户的其他设备上。

iCloud 容器还允许存储无需创建文档格式即可轻松访问的键值对。

系统如何识别文件中的内容类型

识别文件内容类型的主要技术有两种:

  • 统一类型标识符 (UTI)
  • 文件扩展名

统一类型标识符是一个字符串,它唯一地标识被认为具有“类型”的一类实体。UTI 为所有应用程序和服务可以识别和依赖的数据提供一致的标识符。它们也比大多数其他技术更灵活,因为您可以使用它们来表示任何类型的数据,而不仅仅是文件和目录。UTI 的例子包括:

  • public.text— 标识文本数据的公共类型。
  • public.jpeg— 标识 JPEG 图像数据的公共类型。
  • com.apple.bundle— 标识捆绑包目录的 Apple 类型。
  • com.apple.application-bundle— 标识捆绑应用程序的 Apple 类型。

每当基于 UTI 的接口可用于指定文件类型时,您应该优先选择该接口而不是其他任何接口。

许多 macOS 界面允许您指定与您要使用的文件或目录相对应的 UTI。

例如,在“打开”面板中,您可以将 UTI 用作文件过滤器,并将用户选择的文件类型限制为您的应用可以处理的文件类型。

几个 AppKit 类,包括NSDocumentNSPasteboardNSImage,都支持 UTI。在 iOS 中,UTI 仅用于指定粘贴板类型。

系统确定给定文件的 UTI 的一种方法是查看其文件扩展名。

文件扩展名是附加到文件末尾的一串字符,并用句点与主文件名分隔。每个唯一的字符串标识一个特定类型的文件。

例如,.strings扩展名标识具有可本地化字符串数据的资源文件,而.png扩展名标识具有便携式网络图形格式的图像数据的文件。

**注意:**由于句点字符在 macOS 和 iOS 文件名中是有效字符,因此只有文件名中最后一个句点之后的字符才被视为文件扩展名的一部分。最后一个句点左侧的所有内容都被视为文件名本身的一部分。

如果您的应用程序定义了自定义文件格式,您应该在您的应用程序文件中注册这些格式和任何关联的文件扩展名Info.plist

CFBundleDocumentTypes密钥指定您的应用可识别并能够打开的文件格式。

任何自定义文件格式的条目都应包括文件扩展名和与文件内容对应的 UTI。

系统使用该信息将具有适当类型的文件定向到您的应用程序。

文件、并发和线程安全

由于与文件相关的操作涉及与硬盘的交互,因此与大多数其他操作相比速度较慢,因此 iOS 和 macOS 中的大多数与文件相关的界面在设计时都考虑到了并发性。一些技术将异步操作结合到它们的设计中,大多数其他技术可以从调度队列或辅助线程安全地执行。

类/技术 笔记
NSFileManager NSFileManager对于大多数任务,从多个后台线程同时使用默认对象是安全的。此规则的唯一例外是与文件管理器的委托交互的任务。将文件管理器对象与委托一起使用时,建议您创建该类的唯一实例NSFileManager并将委托与该实例一起使用。然后,您应该一次从一个线程使用您的唯一实例。
GCD GCD 本身可以安全地从任何线程使用。但是,您仍然有责任以线程安全的方式编写您的块。
NSFileHandle, NSData, Cocoa streams 大多数用于读取和写入文件数据的 Foundation 对象都可以在任何单个线程中使用,但不应同时在多个线程中使用。
Open and Save panels 因为它们是用户界面的一部分,所以您应该始终从应用程序的主线程中显示和操作打开和保存面板。
POSIX 线程 用于操作文件的 POSIX 线程通常被设计为从任何线程安全地操作。有关详细信息,请参阅相应的手册页。
NSURLNSString 用于指定路径的不可变对象可以安全地从任何线程使用。因为它们是不可变的,所以您也可以同时从多个线程引用它们。当然,这些对象的可变版本一次只能在一个线程中使用。
NSEnumerator及其子类 枚举器对象可以安全地从任何单个线程使用,但不应同时从多个线程使用。

即使您使用线程安全接口来操作文件,当多个线程或多个进程试图对同一个文件进行操作时,问题仍然会出现。

尽管有防止多个客户端同时修改文件的保护措施,但这些保护措施并不总是保证始终对文件进行独占访问。