IOS 沙盒,NSFileManager,NSFileHandl
为了安全起见,iOS应用程序与文件系统的交互仅限于应用程序的沙盒目录中的目录,一般来说在沙盒中存储和读取数据,是需要先获取到沙盒路径;
以下这些方法都是获取对应的沙盒路径
NSHomeDirectory()根据平台的不同,返回用户或应用程序的主目录的路径。
iOS都是 /var/mobile/Containers/Data/Application/系统为你的应用随机生成的目录名(每次启动都会变) -- 也就是沙盒路径
Mac 不太一样:/Users/mjf/Library/Containers/mjf.demo.FileManagerMacDemo/Data
NSTemporaryDirectory()返回沙盒下的tmp目录
IOS :/private/var/mobile/Containers/Data/Application/系统为你的应用随机生成的目录名(每次启动都会变)/tmp/
Mac :/var/folders/tr/7b954qfd3h3f5nvbj09hlc1h0000gn/T/jf.demo.FileManagerMacDemo/
NSOpenStepRootDirectory()返回系统根目录
iOS 和Mac 都是: /
NSHomeDirectoryForUser(NSString * _Nullable userName)返回指定用户home目录,传nil值默认是当年登录用户的home 和NSHomeDirectory()一样
iOS : 返回沙盒目录,和 NSHomeDirectory() 一致
Mac: 和Mac平台的NSHomeDirectory() 返回一致
下面这个方法会去指定目录下搜索路径,得到一个数组
NSArray<NSString *> *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde)
第一个参数 NSSearchPathDirectory: 指定搜索文件
第二个参数 NSSearchPathDomainMask :指定搜索区域 (当前app,系统,网络,全部)
第三个参数 expandTilde :是否展开 ~ ,如果搜索出的路径前面有 ~ ,设置YES可以展开绝对路径
一、沙盒-参考文档
沙盒逻辑图:来自官方文档 image.png也可以通过Xcode->Window->Devices and Simulators->找到你的app->下载下来查看包内容:可以看到沙盒下现有的文件夹
image.png
1.获取沙盒路径
前面提到的NSHomeDirectory()
直接返回沙盒主目录,图中的 Data Container目录
/var/mobile/Containers/Data/Application/系统随机分配的app ID,例如:5F7BEE87-7CD9-4A03-9C09-D09CC428C511
- 指定沙盒下的某个目录:例如Documents, 具体的目录可以command + 右键 跳过去查看
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES)
注:取沙盒路径的搜索范围只能是NSUserDomainMask
/var/mobile/Containers/Data/Application/5F7BEE87-7CD9-4A03-9C09-D09CC428C511/Documents
3.这几个目录的区别:
Documents:
使用此目录存储用户生成的内容。可以通过文件共享使该目录的内容对用户可用。因此,此目录应仅包含您可能希望向用户公开的文件。
该目录的内容由iTunes和iCloud备份。
Library:下有两个子目录 Preferences
,Caches
这是用户数据文件的所有文件的顶级目录。您通常将文件放在几个标准子目录之一中。iOS应用程序通常使用“Application Support”和“Caches”子目录。但是,您可以创建自定义子目录。
Preferences
:此目录包含特定于应用程序的首选项文件。您不应该自己在此目录中创建文件。而是使用NSUserDefaults类或CFPreferences API来获取和设置应用程序的首选项值。在iOS中,此目录的内容由iTunes和iCloud备份。
Caches
:使用此目录可以编写可轻松重新创建的任何特定于应用程序的支持文件。您的应用通常负责管理该目录的内容,并根据需要添加和删除文件。
在iOS 2.2及更高版本中,iTunes或iCloud不会备份此目录的内容。此外,在完全恢复设备期间,系统会删除此目录中的文件。
在iOS 5.0及更高版本中,当系统磁盘空间非常低时,系统可能会删除Caches目录。运行应用程序时永远不会发生这种情况。但是,请注意,从备份还原并不一定是可以删除Caches目录的唯一条件。
tmp:
使用此目录可以编写不需要在应用程序启动之间保留的临时文件。当不再需要文件时,您的应用应从该目录中删除文件;但是,当您的应用未运行时,系统可能会清除此目录。
iTunes或iCloud不会备份此目录的内容。
二、NSFileManager
Foundation提供了三个与沙箱一起使用的框架。NSFileManager、NSFileHandle、NSData
FileManager 类负责管理目录。它的任务包括创建,移动,读取,写入和删除文件路径;
NSFileHandle 类,负责创建对应的文件句柄,对文件内容进行操作
NSData 读取或动态写入内容的Data
1. 创建目录,文件
通过系统方法获取到的沙盒路径都是字符串的形式,如果需要用到URL的形式,则需要转化
+ (NSURL *)fileURLWithPath:(NSString *)path;
1.1 获取沙盒路径,比如:在Documents下创建 Country 目录;
NSString *filePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
1.2 拼接好文件路径
NSString * fileName = [filePath stringByAppendingPathComponent:@"Country"]
1.3 创建文件管理者
NSFileManager *fileManager = [NSFileManager defaultManager];
1.4 创建目录
参数path
,步骤2拼接好的路径;。
参数createIntermediates
是否创建中间路径,如果路径中上的中间某个路径没有,YES会创建,NO直接创建失败。
参数attributes
文件的umask 属性,传nil 当前进程的默认属性集。
参数 error
错误信息,创建成功该值为空。
-(BOOL)createDirectoryAtPath:(NSString *)path
withIntermediateDirectories:(BOOL)createIntermediates attributes:(nullable NSDictionary<NSFileAttributeKey, id> *)attributes error:(NSError **)error
NSError *err;
[fileManager createDirectoryAtPath:fileName
withIntermediateDirectories:NO
attributes:nil
error:&err];
第二个方法路径传的是NSURL *url,这里需要传完整的文件URL
-(BOOL)createDirectoryAtURL:(NSURL *)url
withIntermediateDirectories:(BOOL)createIntermediates attributes:(nullable NSDictionary<NSFileAttributeKey, id> *)attributes error:(NSError **)error
NSError *err;
[fileManager createDirectoryAtURL:[NSURL fileURLWithPath:fileName]
withIntermediateDirectories:YES
attributes:nil
error:&err];
1.4 创建文件,比如: 文本类型
拼接文件到路径, fileName 是上文的
NSString * cityName = [fileName stringByAppendingPathComponent:@"city"];
创建文件:
参数 path,就是拼接好的cityName
参数 data,要写入的内容,NSData类型,传nil 就是一个空文件
参数 attr,文件的umask属性,传nil 当前进程的默认属性集
-(BOOL)createFileAtPath:(NSString *)path contents:(nullable NSData *)data attributes:(nullable NSDictionary<NSFileAttributeKey, id> *)attr;
NSString *context = @"这是个文件";
[fileManager createFileAtPath:fName
contents:[context dataUsingEncoding:NSUTF8StringEncoding]
attributes:nil];
也可以通过: writeToFile 方法创建
-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error;
NSError *err5;
NSString *context = @"这是个文件";
[context writeToFile:fName
atomically:YES
encoding:NSUTF8StringEncoding
error:&err5];
2. NSFileManagerDelegate,这个代理提供了一些在对文件进行操作时的交互接口,以下方法需要先遵守协议才会被调用(如果多线程下使用,需要创建单独实例)
对应的方法有,复制,移动,删除时的接口,还有执行失败时交互的接口
2.1 复制文件或目录的时候会调用
-(BOOL)fileManager:(NSFileManager *)fileManager shouldCopyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath
2.2 复制时发生错误,会调用
-(BOOL)fileManager:(NSFileManager *)fileManager shouldProceedAfterError:(NSError *)error copyingItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath
3.多线程情况下:
由于与文件相关的操作涉及与硬盘的交互,因此与大多数其他操作相比,速度较慢
[NSFileManager defaultManager] 创建共享对象,单例;多线程下是安全的
[[NSFileManager alloc]init] 单独实例,如果使用delegate接收有关移动,复制,删除和链接操作状态的通知,则需要创建单独实例。
4.其他常用的操作:
1.判断文件是否存在
-(BOOL)fileExistsAtPath:(NSString *)path
2.判断文件是不是存在, isDirectory表示目录还是文件
-(BOOL)fileExistsAtPath:(NSString *)path isDirectory:(nullable BOOL *)isDirectory
3.从path的文件读取内容,如果path是目录或者发生其他错误,返回nil
-(nullable NSData *)contentsAtPath:(NSString *)path;
4.删除文件
-(BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
5.列出当前路径下的所有项
-(nullable NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error
6.列出当前路径下的所有项,包括子目录的目录(递归遍历)
-(nullable NSArray<NSString *> *)subpathsOfDirectoryAtPath:(NSString *)path error:(NSError **)error
7.移动目录或文件, toPath不能存在
-(BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
8.复制目或录文件,toPath不能存在
-(BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
三、NSFileHandle 文件句柄
注意:writeToFile 方法每次都会覆盖上一次的内容,所以如果需要追加或其他更加精细的操作,是需要通过文件句柄控制的
1.文件写入、追加
1.1 获取文件句柄,写操作需要获取写对应的句柄
+(nullable instancetype)fileHandleForWritingAtPath:(NSString *)path
//先需要正确的获取文件路径
NSString *filePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES).firstObject;
filePath = [filePath stringByAppendingPathComponent:@"word"];
//获取文件句柄
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
1.2 写入
-(BOOL)writeData:(NSData *)data error:(out NSError **)error
NSError *writeErr;
[fileHandle writeData:[@"hello world!" dataUsingEncoding:NSUTF8StringEncoding] error:&writeErr];
1.3追加,需要移动文件指针到文件尾
-(BOOL)seekToEndReturningOffset:(out unsigned long long *_Nullable)offsetInFile error:(out NSError **)error
NSError *offsetErr;
unsigned long long * offset = NULL;
//文件指针移动到文件末尾
[fileHandle seekToEndReturningOffset:offset error:&offsetErr];
//追加
[fileHandle writeData:[@"哈哈哈" dataUsingEncoding:NSUTF8StringEncoding] error:&writeErr];
1.4 读取全部文件内容
读取文件内容
-(nullable NSData *)readDataToEndOfFileAndReturnError:(out NSError **)error
NSError *readErr;
NSData *data = [fileHandle readDataToEndOfFileAndReturnError:&readErr];
或者,直接通过NSData的初始化方法
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSString *readData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
1.5读取指定长度的内容
-(nullable NSData *)readDataUpToLength:(NSUInteger)length error:(out NSError **)error
NSError *readErr;
NSData *data = [fileHandle readDataUpToLength:10 error:&readErr];
NSString *readData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
1.6 关掉文件句柄
-(BOOL)closeAndReturnError:(out NSError **)error
NSError *writeErr;
[fileHandle closeAndReturnError:&writeErr];