iOS文件系统

沙盒机制

概念

iOS 沙盒机制是一种安全策略,它将每个应用程序的数据和资源隔离在一个专用目录中,限制了应用程序访问其他应用程序或系统文件的能力,从而保护了用户数据和系统安全.

目录结构

For security purposes, an iOS app's interactions with the file system are limited to the directories inside the app's sandbox directory. During installation of a new app, the installer creates a number of container directories for the app inside the sandbox directory. Each container directory has a specific role. The bundle container directory holds the app's bundle, whereas the data container directory holds data for both the app and the user. The data container directory is further divided into a number of subdirectories that the app can use to sort and organize its data. The app may also request access to additional container directories---for example, the iCloud container---at runtime.

👆大概内容讲的是出于安全考虑,iOS应用只能在当前APP的沙盒目录下与文件系统进行交互(读取、创建、删除等).在APP安装时,系统会在当前APP的沙盒目录下创建不同类型不同功能的容器目录(Bundle、Data、iCloud).Data Container又进一步被划分为Documents、Library、temp、System Data.沙盒目录结构如下图所示:

各目录描述如下图所示:

下面介绍一些关于文件系统中常用的API.

创建NSFileManager

objectivec 复制代码
// 1.创建实例
NSFileManager *fileManager = [[NSFileManager alloc] init];

// 2.获取单例
NSFileManager *fileManager = [NSFileManager defaultManager];

// 3.自定义
自定义NSFileManager实现一些自定义功能.

NSFileManager有一个delegate(NSFileManagerDelegate)属性,实现该协议的对象能够对文件的拷贝、移动等操作做更多的逻辑处理,同时能在这些操作发生错误时做一些容错的处理.

objectivec 复制代码
// 该协议用于控制文件/文件夹,是否允许移动、删除、拷贝.以及允许这些操作发生错误时进行额外的处理.
@protocol NSFileManagerDelegate <NSObject>

// 控制是否允许该操作:
// 以下每种方法都有一个NSURL和NSString的版本,URL和Path同作用的方法只会调用一次,并且优先调用URL的方法.如果两个方法都没实现,则实现系统的默认值YES.
// 其中srcURL/srcPath分别代表原(文件/文件夹)路径URL(file://xxx)/路径(xxx). xxx为完整路径.
// 其中dstURL/dstPath分别代表目标(文件/文件夹)路径URL(file://xxx)/路径(xxx). xxx为完整路径.

// 错误处理:
// 以下每种方法都有一个NSURL和NSString的版本,URL和Path同作用的方法只会调用一次,并且优先调用URL的方法.如果两个方法都没实现,则不处理错误.

@optional

/// Moving an Item

// 在移动文件/文件夹前调用,控制是否允许移动操作
// 如果两个方法都没有实现,系统默认YES即允许移动. 
- (BOOL)fileManager:(NSFileManager *)fileManager shouldMoveItemAtURL:(NSURL *)srcURL toURL:(NSURL *)dstURL;
- (BOOL)fileManager:(NSFileManager *)fileManager shouldMoveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath;

// 移动失败时调用 处理错误信息
- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error movingItemAtURL:(NSURL *)srcURL toURL:(NSURL *)dstURL;
- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error movingItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath;

/// Copy an Item

// 在拷贝文件/文件夹前调用,控制是否允许拷贝操作
// 如果两个方法都没有实现,系统默认YES即允许拷贝. 
- (BOOL)fileManager:(NSFileManager *)fileManager shouldCopyItemAtURL:(NSURL *)srcURL toURL:(NSURL *)dstURL;
- (BOOL)fileManager:(NSFileManager *)fileManager shouldCopyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath;

// 拷贝失败时调用 处理错误信息
- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error copyingItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath;
- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error copyingItemAtURL:(NSURL *)srcURL toURL:(NSURL *)dstURL;

/// Delete an Item

// 在拷贝文件/文件夹前调用,控制是否允许删除操作
// 如果两个方法都没有实现,系统默认YES即允许删除. 
- (BOOL)fileManager:(NSFileManager *)fileManager shouldRemoveItemAtPath:(NSString *)path;
- (BOOL)fileManager:(NSFileManager *)fileManager shouldRemoveItemAtURL:(NSURL *)URL;

// 删除失败时调用 处理错误信息
- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error removingItemAtPath:(NSString *)path;
- (BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error removingItemAtURL:(NSURL *)URL;

@end

考虑线程安全的问题,如果需要实现NSFileManagerDelegate协议,通常是新创建NSFileManager实例或是继承NSFileManager后新建实例,确保一个一个实例仅对应一个delegate.

创建文件/文件夹

objectivec 复制代码
/// 文件

// 创建文件,会覆盖已存在的文件内容.
// path: 文件路径.
// data: 文件内容.
// attr: 文件属性.例如:设置文件夹可读写权限、文件夹创建日期中.传入nil,则使用系统默认的配置.
// return: YES:文件存在或创建成功. NO:文件创建失败
- (BOOL)createFileAtPath:(NSString *)path 
                contents:(NSData *)data 
              attributes:(NSDictionary<NSFileAttributeKey, id> *)attr;

/// 文件夹

// url: 文件夹路径URL.
// createIntermediate: 是否自动创建目录中不存在的中间目录,如果设置为NO,仅仅会创建路径的最后一级目录,若任意中间路径不存在,该方法会返回NO.同时如果任意中间目录是文件也会失败.
// attributes: nil,则使用系统默认的配置.
// error: 错误信息.
// YES: 文件夹创建成功.YES: createIntermediates为YES且文件夹已存在. NO: 错误发生.
- (BOOL)createDirectoryAtURL:(NSURL *)url 
 withIntermediateDirectories:(BOOL)createIntermediates 
                  attributes:(NSDictionary<NSFileAttributeKey, id> *)attributes 
                       error:(NSError * _Nullable *)error;

// 同上
- (BOOL)createDirectoryAtPath:(NSString *)path 
  withIntermediateDirectories:(BOOL)createIntermediates 
                   attributes:(NSDictionary<NSFileAttributeKey, id> *)attributes 
                        error:(NSError * _Nullable *)error;

其他方式写入文件:NSData、NSString、All kinds Of Collections (写入plist).

删除文件/文件夹

删除操作是指将文件/文件夹从指定目录移除掉.

objectivec 复制代码
// 以file://xxx的形式删除文件/文件夹.
// srcURL: 原文件/文件夹目录URL
// dstURL: 目标目录URL.
// error: 错误信息.
// return: YES: 移动成功或URL为nil或delegate停止删除操作. NO: 错误发生.
- (BOOL)removeItemAtURL:(NSURL *)URL 
                  error:(NSError * _Nullable *)error;
// 同上,不过传入的是xxx完整路径.
- (BOOL)removeItemAtPath:(NSString *)path 
                   error:(NSError * _Nullable *)error;

// 将文件/文件夹移入到废纸篓,适用于Macos.iOS上使用会失败.
- (BOOL)trashItemAtURL:(NSURL *)url 
      resultingItemURL:(NSURL * _Nullable *)outResultingURL 
                 error:(NSError * _Nullable *)error;
                 
// 注意: 
// 1.如果删除的是文件夹,则会删除文件夹中所有的内容. 
// 2.删除文件前会调用delegate的-[fileManager:shouldRemoveItemAtURL:]或-[fileManager:shouldRemoveItemAtPath:]方法,用于控制能否删除.如果都没有实现,则默认可以删除. 
// 3.删除失败时会调用delegate的-[fileManager:shouldProceedAfterError:removingItemAtURL:]或-[fileManager:shouldProceedAfterError:removingItemAtPath:]方法,用于处理错误. 
// 如果2个方法都没有实现则删除失败.并且删除方法会返回相应的error信息. 
// 方法返回YES会认为删除成功,返回NO则删除失败,删除方法接收error信息.

文件/文件夹是否存在

objectivec 复制代码
// path: 文件/文件夹路径.
// isDirectory: YES: 当前为文件夹. NO: 当前为文件.
// return: YES: 文件/文件夹存在. NO: 文件/文件夹不存在.
- (BOOL)fileExistsAtPath:(NSString *)path 
             isDirectory:(BOOL *)isDirectory;

// 同上,不过不能判断当前是文件还是文件夹.
- (BOOL)fileExistsAtPath:(NSString *)path;

遍历文件夹

有时候我们并不知道文件的具体路径,此时就需要遍历文件夹去拼接完整路径.

objectivec 复制代码
/// 浅度遍历: 返回当前目录下的文件/文件夹(包括隐藏文件).
// 默认带上了options: NSDirectoryEnumerationSkipsSubdirectoryDescendants.
// NSDirectoryEnumerationIncludesDirectoriesPostOrder无效,因为不会遍历子目录.
- (nullable NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)path 
																							        error:(NSError **)error

// url: 文件路径.
// keys: 预请求每个文件属性的key数组.如果不想预请求则传入@[],传nil会有默认的预请求keys.
// options: 遍历时可选掩码.
// error: 错误信息.
- (nullable NSArray<NSURL *> *)contentsOfDirectoryAtURL:(NSURL *)url 
														 includingPropertiesForKeys:(nullable NSArray<NSURLResourceKey> *)keys 
																								options:(NSDirectoryEnumerationOptions)mask 
																									error:(NSError **)error

/// 深度遍历(递归遍历): 返回当前目录下的所有文件/文件夹(包括隐藏文件).
- (nullable NSDirectoryEnumerator<NSString *> *)enumeratorAtPath:(NSString *)path
- (nullable NSDirectoryEnumerator<NSURL *> *)enumeratorAtURL:(NSURL *)url 
																	includingPropertiesForKeys:(nullable NSArray<NSURLResourceKey> *)keys 
																										 options:(NSDirectoryEnumerationOptions)mask 
																								errorHandler:(nullable BOOL (^)(NSURL *url, NSError *error))handler
- (NSArray<NSString *> *)subpathsOfDirectoryAtPath:(NSString *)path 
                                             error:(NSError * _Nullable *)error;
- (NSArray<NSString *> *)subpathsAtPath:(NSString *)path;

获取文件夹

objectivec 复制代码
// iOS基本上都是使用NSUserDomainMask.

// 获取指定目录类型和mask的文件夹.类似于NSSearchPathForDirectoriesInDomains方法.
// 常用的directory:
// NSApplicationSupportDirectory -> Library/Application Support.
// NSCachesDirectory -> /Library/Caches.
// NSLibraryDirectory -> /Library.
- (NSArray<NSURL *> *)URLsForDirectory:(NSSearchPathDirectory)directory 
														 inDomains:(NSSearchPathDomainMask)domainMask

// 获取指定directory & domainMask下的文件夹.
// domain: 不能传入NSAllDomainsMask.
// url: 仅当domain = NSUserDomainMask & directory = NSItemReplacementDirectory时有效.
// shouldCreate: 文件不存在时 是否创建.
- (NSURL *)URLForDirectory:(NSSearchPathDirectory)directory 
                  inDomain:(NSSearchPathDomainMask)domain 
         appropriateForURL:(NSURL *)url 
                    create:(BOOL)shouldCreate 
                     error:(NSError * _Nullable *)error;
// 同上
FOUNDATION_EXPORT NSArray<NSString *> *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);
// 获取/temp目录
FOUNDATION_EXPORT NSString *NSTemporaryDirectory(void);
// 获取沙盒根目录
FOUNDATION_EXPORT NSString *NSHomeDirectory(void);

设置和获取文件/文件夹属性

objectivec 复制代码
// 获取指定目录下文件/文件夹的属性[NSFileAttributeKey].(https://developer.apple.com/documentation/foundation/nsfileattributekey).
- (nullable NSDictionary<NSFileAttributeKey, id> *)attributesOfItemAtPath:(NSString *)path 
                                                                    error:(NSError **)error;

// 为指定目录文件/文件夹设置属性.
- (BOOL)setAttributes:(NSDictionary<NSFileAttributeKey, id> *)attributes ofItemAtPath:(NSString *)path error:(NSError * _Nullable *)error;

// 基于NSDictionary提供的便利方法 //
@interface NSDictionary<KeyType, ObjectType> (NSFileAttributes)

// 文件大小
- (unsigned long long)fileSize;

// 文件创建日期
- (nullable NSDate *)fileCreationDate;
// 文件修改日期
- (nullable NSDate *)fileModificationDate;

// 文件类型.NSFileAttributeType
- (nullable NSString *)fileType;

// 文件权限掩码
- (NSUInteger)filePosixPermissions; // 位掩码 可见文件 _s_ifmt.h eg:S_IRWXU
/* File mode */
/* Read, write, execute/search by owner */
#define S_IRWXU         0000700         /* [XSI] RWX mask for owner */
#define S_IRUSR         0000400         /* [XSI] R for owner */
#define S_IWUSR         0000200         /* [XSI] W for owner */
#define S_IXUSR         0000100         /* [XSI] X for owner */
/* Read, write, execute/search by group */
#define S_IRWXG         0000070         /* [XSI] RWX mask for group */
#define S_IRGRP         0000040         /* [XSI] R for group */
#define S_IWGRP         0000020         /* [XSI] W for group */
#define S_IXGRP         0000010         /* [XSI] X for group */
/* Read, write, execute/search by others */
#define S_IRWXO         0000007         /* [XSI] RWX mask for other */
#define S_IROTH         0000004         /* [XSI] R for other */
#define S_IWOTH         0000002         /* [XSI] W for other */
#define S_IXOTH         0000001         /* [XSI] X for other */

// 当前文件/文件夹所处的文件系统编号
- (NSInteger)fileSystemNumber;
这两个方法可以拼接文件的引用URL -> file:///.file/id=fileSystemNumber.fileSystemFileNumber
// 当前文件/文件夹在文件系统中的编号
- (NSUInteger)fileSystemFileNumber;

// 是否是隐藏文件
- (BOOL)fileExtensionHidden;
...

@end

移动文件/文件夹

移动操作是将文件从一个位置移动到另一个位置.

objectivec 复制代码
// 以file://xxx的形式移动文件/文件夹.
// srcURL: 原文件/文件夹位置URL.
// dstURL: 目标位置URL.
// error: 错误信息.
// return: YES: 移动成功或manager的delegate停止移动操作. NO: 错误发生.
- (BOOL)moveItemAtURL:(NSURL *)srcURL 
                toURL:(NSURL *)dstURL 
                error:(NSError * _Nullable *)error;
// 同上,不过传入的是xxx完整路径.
- (BOOL)moveItemAtPath:(NSString *)srcPath 
                toPath:(NSString *)dstPath 
                 error:(NSError * _Nullable *)error;

// 注意:
// 1.srcURL/srcPath、dstURL/dstPath任意为nil会crash.
// 2.如果目标目录文件已存在会被覆盖.
// 3.移动文件前会调用delegate的-[fileManager:shouldMoveItemAtURL:toURL:]或-[fileManager:shouldMoveItemAtPath:toPath:]方法,用于控制能否移动.如果都没有实现,则默认可以移动.
// 4.移动失败时会调用delegate的-[fileManager:shouldMoveItemAtURL:toURL:]或-[fileManager:shouldMoveItemAtPath:toPath:]方法,用于处理错误.
// 如果2个方法都没有实现则移动失败.并且移动方法会返回相应的error信息.
// 方法返回YES会认为移动成功,返回NO则移动失败,移动方法接收error信息.

拷贝文件/文件夹

拷贝操作是将原文件从一个位置copy到另一个位置,类似于复制粘贴.

objectivec 复制代码
// 以file://xxx的形式拷贝文件/文件夹.
// srcURL: 原文件/文件夹/位置URL.
// dstURL: 目标位置URL.
// error: 错误信息.
// return: YES: 拷贝成功或manager的delegate停止拷贝操作. NO: 错误发生.
- (BOOL)copyItemAtURL:(NSURL *)srcURL 
                toURL:(NSURL *)dstURL 
                error:(NSError * _Nullable *)error;
// 同上,不过传入的是xxx完整路径.
- (BOOL)copyItemAtPath:(NSString *)srcPath 
                toPath:(NSString *)dstPath 
                 error:(NSError * _Nullable *)error;

// 注意:
// 1.srcURL/srcPath、dstURL/dstPath任意为nil会crash.
// 2.如果目标目录已经存在则会发生错误.
// 3.拷贝文件前会调用delegate的-[fileManager:shouldCopyItemAtURL:toURL:]或-[fileManager:shouldCopyItemAtPath:toPath:]方法,用于控制能否拷贝.如果都没有实现,则默认可以拷贝.
// 4.拷贝失败时会调用delegate的-[fileManager:shouldProceedAfterError:copyingItemAtURL:toURL:]或-[fileManager:shouldProceedAfterError:copyingItemAtPath:toPath:]方法,用于处理错误.
// 如果2个方法都没有实现则拷贝失败.并且拷贝方法会返回相应的error信息.
// 方法返回YES会认为拷贝成功,返回NO则拷贝失败,拷贝方法接收error信息.

参考资料

  1. developer.apple.com/library/arc...
  2. developer.apple.com/documentati...

好物推荐

  1. OpenSim:用于快速定位模拟器中项目沙盒目录.

  2. Flex:用于真机或模拟器Debug环境下调试.

相关推荐
Fan_web12 分钟前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常14 分钟前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇1 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr1 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho2 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常3 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
小白学习日记4 小时前
【复习】HTML常用标签<table>
前端·html
丁总学Java4 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele4 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范