版本
Xcode 8.2.1
一、NSHomeDirectory()
目录(Directories)在现在的操作系统里就是文件夹,而路径(Path)则是包括盘符及一个或多个文件夹。
iOS每个APP都会有一个沙盒(Sandbox),用于存储该APP所产生的资料内容,如图片、视频、文件夹、plist文件等等。而这个沙盒的路径可由NSHomeDirectory()得到,也叫APP的根目录。根目录下有四个文件/文件夹:
- Documents 目录:Apple官方建议将APP的重要数据保存到这个目录下。因为iTunes备份时包括此目录。
- MyApp.app 目录:这是APP本身。一般不要对其更改,否则启动时容易崩溃。
- Library 目录:该目录下有两个子目录:Preferences和Caches;
- Preferences:包含APP的偏好设置。可用NSUserDefaults类来获取或设置;
- Caches:APP专用的支持文件,保存APP再次启动过程中需要的信息。
- tmp 目录:存放临时文件,APP关闭后,该目录下文件将被清除。
接下来我们小试一把,通过NSHomeDirectory()获取APP的根目录,并对其目录路径搞点小动作。
int main(int argc, char * argv[]) {
//获取根目录
NSString *userPath = NSHomeDirectory();
NSLog(@"path = %@",userPath);
//拼接路径
NSString *newPath = [userPath stringByAppendingFormat:@"%@",@"/test"];
NSLog(@"newPath = %@",newPath);
//添加子路径
NSString *newPath2 = [userPath stringByAppendingPathComponent:@"test"];
NSLog(@"newPath2 = %@",newPath2);
//切割路径x
NSArray *resultPath = [userPath pathComponents];
NSLog(@"resultPath = %@",resultPath);
//再次拼接路径
NSString *newPath1 = [userPath stringByAppendingFormat:@"%@",@"/test/lalala.jpg"];
NSLog(@"newPath1 = %@",newPath1);
//获取文件后缀名
NSString *resultStr = [newPath1 pathExtension];
NSLog(@"resultStr = %@",resultStr);
//删除后缀名
NSString *resultPath1 = [newPath1 stringByDeletingPathExtension];
NSLog(@"resultPath1 = %@",resultPath1);
//删除最后一个子路径
NSString *resultPath2 = [newPath1 stringByDeletingLastPathComponent];
NSLog(@"resultPath2 = %@",resultPath2);
}
收获如下:
获取目录路径的方法小结:
// 获取根目录
NSString *homeDir = NSHomeDirectory();
NSLog(@"homeDir=%@", homeDir);
// homeDir=/var/mobile/Containers/Data/Application/D3765F06-44E4-4B04-8CB0-2E92B7F07997
// 获取Documents目录
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [docPaths objectAtIndex:0];
NSLog(@"docDir=%@", docDir);
// docDir=/var/mobile/Containers/Data/Application/D3765F06-44E4-4B04-8CB0-2E92B7F07997/Documents
// 获取Caches目录
NSArray *cachePaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDir = [cachePaths objectAtIndex:0];
NSLog(@"cachesDir=%@", cachesDir);
// cachesDir=/var/mobile/Containers/Data/Application/D3765F06-44E4-4B04-8CB0-2E92B7F07997/Library/Caches
// 获取tmp目录
NSString *tmpDir = NSTemporaryDirectory();
NSLog(@"tmpDir=%@", tmpDir);
// tmpDir=/private/var/mobile/Containers/Data/Application/D3765F06-44E4-4B04-8CB0-2E92B7F07997/tmp/
注意:
使用NSTemporaryDirectory()方法获取tmp目录的时候, 后面多了一个/, 后面多了一个/, 后面多了一个/.
二、NSFileManager
对于这些文件路径的操作(移动、复制、连接或者删除等等),往往会很杂乱,有时甚至会操作出错。Apple为我们提供一个类,用于管理这些文件(路径),是为——NSFileManager文件管理器。
NSFileManager类支持NSString和NSURL(往后再介绍)作为文件路径。它还拥有一个代理协议NSFileManagerDelegate用来接收各种文件操作的通知,典型功用是当错误发生时可决定是否继续。
NSFileManager可以在多个线程中安全调用,但问题是,如果创建了不一样的NSFileManager实例对象,那么代理方法由谁来实现?别担心,苹果早就想好了——NSFileManager类的实例对象为单例。
所谓单例,即,不管你用这个类创建了多少个实例对象,这些对象实际上都是同一个(名称虽不同,指针却一样)!下面举例论证:
int main(int argc, char * argv[]) {
//获取文件管理器,单例对象:在整个应用程序当中,只会实例化一个这种类型的对象
NSFileManager *manager = [NSFileManager defaultManager];
NSLog(@"manager指针:%p",manager);
//manager和manager1指针一样,指向同一个对象
NSFileManager *manager1 = [NSFileManager defaultManager];
NSLog(@"manager1指针:%p",manager1);
}
我们不一样?有啥不一样:
前面介绍了获取APP本身的一些目录,下面为了方便查看结果,拷贝操作部分,我们将对桌面目录试验NSFileManager。先在桌面放置一个plist文件,然后右键点“显示简介”,在“位置”那里得到路径。续上代码:
int main(int argc, char * argv[]) {
//获取文件管理器,单例对象:在整个应用程序当中,只会实例化一个这种类型的对象
NSFileManager *manager = [NSFileManager defaultManager];
NSLog(@"manager指针:%p",manager);
//manager和manager1指针一样,指向同一个对象
NSFileManager *manager1 = [NSFileManager defaultManager];
NSLog(@"manager1指针:%p",manager1);
//获取tmp目录
NSString *TmpDir = NSTemporaryDirectory();
//拼接路径
NSString *TestDir = [TmpDir stringByAppendingFormat:@"%@",@"/test"];
//fileExistsAtPath判断此路径是否已经存在
if(![manager fileExistsAtPath:TestDir]) {
//先声明一个NSError指针
NSError *error = nil;
//创建目录----createDirectoryAtPath
[manager createDirectoryAtPath:TestDir //参数1: 创建的目录路径
withIntermediateDirectories:YES //参数2: 是否自动添加缺失的路径
attributes:nil //参数3: 创建文件的附带信息,一般为nil
error:&error]; //参数4: 错误信息;二级指针
//如果存在error,就打印错误信息
if(error) {
NSLog(@"error = %@",error);
}
}
//浅层遍历(遍历根目录第一层文件和文件夹)
NSString *HomeDir = NSHomeDirectory();
NSArray *resultArray = [manager contentsOfDirectoryAtPath:HomeDir error:nil];
for(id obj in resultArray) {
NSLog(@"浅层遍历obj = %@",obj);
}
//深层遍历(遍历tmp路径下的所有文件夹和文件)
NSArray *resultArr1 = [manager subpathsOfDirectoryAtPath:HomeDir error:nil];
for(id obj1 in resultArr1) {
NSLog(@"深层遍历obj = %@",obj1);
}
//创建一个桌面文件夹test
NSString *desDir = @"/Users/tailor/Desktop/test";
if(![manager fileExistsAtPath:desDir]) {
NSError *error = nil;
[manager createDirectoryAtPath:desDir
withIntermediateDirectories:YES
attributes:nil
error:&error];
if(error) {
NSLog(@"error = %@",error);
}
}
//拷贝文件目录
NSString *srcPath = @"/Users/tailor/Desktop/Info.plist";
NSString *dstPath1 = @"/Users/tailor/Desktop/Info1.plist";
if(![manager copyItemAtPath:srcPath toPath:dstPath1 error:nil]) {
NSLog(@"拷贝失败");
}
//移动/剪切
NSString *dstPath2 = @"/Users/tailor/Desktop/test/Info2.plist"; //test必须存在,Info2.plist不存在(还没创建)
if(![manager moveItemAtPath:dstPath1 toPath:dstPath2 error:nil]) {
NSLog(@"移动失败");
}
//删除
if(![manager removeItemAtPath:srcPath error:nil]) {
NSLog(@"删除失败");
}
}
结果如下:
对于copyItemAtPath: toPath:和moveItemAtPath: toPath:方法的一些操作,本人之前踩了许多坑,现在把它铺平。先来看看官方文档:
Discussion
If srcPath is a file, the method creates a file at dstPath that holds the exact contents of the original file (this includes BSD special files). If srcPath is a directory, the method creates a new directory at dstPath and recursively populates it with duplicates of the files and directories contained in srcPath, preserving all links. The file specified in srcPath must exist, while dstPath must not exist prior to the operation. When a file is being copied, the destination path must end in a filename—there is no implicit adoption of the source filename. Symbolic links are not traversed but are themselves copied. File or directory attributes—that is, metadata such as owner and group numbers, file permissions, and modification date—are also copied.
需要注意的信息有(无聊数了下有5个必须):
- 如果源路径(srcPath)是文件,则目标路径(dstPath)必须是文件(且后缀名一致);
- 如果源路径是文件夹,则目标路径必须是文件夹,将会复制源文件夹下所有文件(夹);
- 方法调用前,源路径必须存在,目标路径必须不存在(注意:目 标文件不存在,但目标文件所在文件夹必须存在);
- 方法的一些错误提示等。
三、后续
前面讨论了使用NSFileManager创建目录(文件夹), 其中withIntermediateDirectories:YES, 可以自动添加缺失的路径. 例如, 我们创建一个tmp/aaa/bbb目录, 假如那个参数设置为NO, 则创建失败, 因为没有aaa这个文件夹; 而如果设置为YES, 则系统自动添加缺失的aaa文件夹路径, 最终创建成功.
那么怎样创建文件呢?
使用以下方法:
NSString *filePath = [NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"123456.avi"];
// 移除之前的
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
// 创建文件
if(![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
BOOL success = [[NSFileManager defaultManager] createFileAtPath:filePath // 文件路径
contents:nil // 初始化的内容
attributes:nil]; // 附加信息
NSLog(@"success:%@, filePath=%@", success ? @"YES" : @"NO", filePath);
}
注意:
创建文件方法和创建文件夹方法一大不同之处在于, 创建文件方法不能跨路径创建文件. 比如说, 将上面的文件路径改为"aaa/123456.avi", 由于多了aaa这个文件夹, 导致创建失败. 也就是说, createFileAtPath:方法不会自动添加缺失的文件夹路径.
创建文件的正确姿势
NSFileManager *manager = [NSFileManager defaultManager];
//获取tmp目录
NSString *TmpDir = NSTemporaryDirectory();
NSString *filePath = [NSString stringWithFormat:@"%@%@", TmpDir, @"abc/123456.avi"];
// 移除之前的filePath
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
// 创建文件夹
NSString *folderPath = [filePath stringByDeletingLastPathComponent]; // 去除最后的组成部分 (/123456.avi)
if(![manager fileExistsAtPath:folderPath]) {
BOOL success = [manager createDirectoryAtPath:folderPath //参数1: 创建的目录路径
withIntermediateDirectories:YES //参数2: 是否自动添加缺失的路径
attributes:nil //参数3: 创建文件的附带信息
error:nil]; //参数4: 错误信息
NSLog(@"创建文件夹 success:%@, folderPath:%@", success ? @"YES" : @"NO", folderPath);
}
// 创建文件
if(![manager fileExistsAtPath:filePath]) {
BOOL success = [manager createFileAtPath:filePath // 文件路径
contents:nil // 初始化的内容
attributes:nil]; // 附加信息
NSLog(@"success:%@, filePath=%@", success ? @"YES" : @"NO", filePath);
}