注册
iOS

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.沙盒目录结构如下图所示:


b55dc5774d52629a79c8efe14bff5208.png


各目录描述如下图所示:


38e90c3f5fe2e17e7ed1aa1ad2911517.png


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


创建NSFileManager

// 1.创建实例
NSFileManager *fileManager = [[NSFileManager alloc] init];

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

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

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

// 该协议用于控制文件/文件夹,是否允许移动、删除、拷贝.以及允许这些操作发生错误时进行额外的处理.
@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.


创建文件/文件夹

/// 文件

// 创建文件,会覆盖已存在的文件内容.
// 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).


删除文件/文件夹


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

// 以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信息.

文件/文件夹是否存在


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

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

遍历文件夹


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

/// 浅度遍历: 返回当前目录下的文件/文件夹(包括隐藏文件).
// 默认带上了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;

获取文件夹

// 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);

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

// 获取指定目录下文件/文件夹的属性[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

移动文件/文件夹


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

// 以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到另一个位置,类似于复制粘贴.

// 以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环境下调试.


作者:小白不是小白
链接:https://juejin.cn/post/7276412839585382419
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册