iOS基础 - NSFileManager | 沙盒 | 归档和解档
▶ 沙盒
我们知道存储在内存中的数据,当程序关闭或内存释放,数据就会丢失,这种数据只是临时的
数据持久化的本质:数据保存成文件,存储在程序的沙盒中
沙盒机制
A. 每个应用程序位于文件系统的严格限制部分,就是说每个应用程序只能在该程序所创建的文件系统中读取文件
B. 每个应用程序在 iOS 系统内都放在了统一的文件夹目录下,沙盒的本质就是一个文件夹,名字是随机分配的
C. 沙盒构成
Document 存储用户数据、需要备份的信息
Library/Caches 存储缓存文件、程序专用的支持文件
Library/Preferences 存储应用程序的偏好设置文件
.app 程序包。iOS 8 后 app 不再存储在沙盒中,有单独的文件夹专门存储所有程序的 app包
tmp 临时文件,比如下载的 zip 包、解压后的再删除
▶ NSFileManager
NSFileManager 可创建文件夹并对文件进行创建、移动、复制、删除操作,判断文件是否存在等等。通过 detaultManager 创建单例对象后使用
1 #import "ViewController.h" 2 @interface ViewController () 3 @property(nonatomic,strong) NSFileManager *fileManager; // 文件管理器 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad{ 10 [super viewDidLoad]; 11 self.fileManager = [[NSFileManager alloc] init]; 12 } 13 14 15 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 16 17 // 沙盒主路径 18 NSString *document = NSHomeDirectory(); 19 NSLog(@"document===%@",document); 20 21 NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 22 // Document文件夹 23 NSString *filePath = array.firstObject; 24 NSLog(@"filePath===%@",filePath); 25 26 27 // 该方法会自动在前一个路径 filePath 的后面加上 / 28 // 如果后面的一个路径 filePath1 的文件名里有 / 则会将 / 去除 29 NSString *filePathAAA = [filePath stringByAppendingPathComponent:@"AAA"]; 30 31 // 在指定的目录下创建文件夹,如果该文件夹存在,则不创建 32 // IntermediateDirectories:是否创建中间文件夹,YES 会创建中间文件夹,NO 不会创建中间 33 // attributes:文件夹的属性 34 [self.fileManager createDirectoryAtPath:filePathAAA withIntermediateDirectories:YES attributes:nil error:nil]; 35 36 37 // 将要读入的内容 38 NSString *content = @"青春如同奔流的江河"; 39 // 在将字符串进行 data 转换时,如果是中文字符,使用 NSASCIIStringEncoding 编码则不会写入到文件 40 // 因为此时生成的 contentData 是 nil 41 // 可以使用 NSUnicodeStringEncoding 或者 NSUTF8StringEncoding 编码 42 NSData *contentData = [content dataUsingEncoding:NSUnicodeStringEncoding]; 43 44 45 // 不创建 test1 和 test2 两文件夹 46 NSString *test1Path = [filePathAAA stringByAppendingPathComponent:[NSString stringWithFormat:@"test1"]]; 47 NSString *test2Path = [filePathAAA stringByAppendingPathComponent:[NSString stringWithFormat:@"test2"]]; 48 49 // 如果路径不存在,则不会写入二进制文件 50 if ([contentData writeToFile:test2Path atomically:NO]) { 51 52 NSLog(@"不会把内容写入到 test2文件 中"); 53 } 54 55 // 文件是否存在 56 if (![self.fileManager fileExistsAtPath:test1Path]) { 57 58 // 判断该文件名存在,包括后缀名:比如 testA 和 testA.html是两个文件。如果存在先将这个文件删除,然后再创建 59 // 如果文件名不存在,则直接创建 60 [self.fileManager createFileAtPath:test1Path contents:contentData attributes:nil]; 61 62 // 复制文件 63 NSError *error; 64 [self.fileManager copyItemAtPath:test1Path toPath:test2Path error:&error]; 65 // 取得文件里的内容 66 NSData *someData = [self.fileManager contentsAtPath:test1Path]; 67 68 }else{ 69 //删除文件 70 [self.fileManager removeItemAtPath:test1Path error:nil]; 71 } 72 73 // 创建 DDD 文件夹 且里面包含 ddd 文件夹 74 NSString *filePathDDD = [[filePath stringByAppendingPathComponent:@"DDD"] stringByAppendingPathComponent:@"ddd"]; 75 [self.fileManager createDirectoryAtPath:filePathDDD withIntermediateDirectories:YES attributes:nil error:nil]; 76 77 // 不会创建 MMM 文件夹,且其中的 mmm 文件夹也不会被创建 78 NSString *filePathB = [[filePath stringByAppendingPathComponent:@"MMM"] stringByAppendingPathComponent:@"mmm"]; 79 [self.fileManager createDirectoryAtPath:filePathB withIntermediateDirectories:NO attributes:nil error:nil]; 80 81 //----------------------------- 文件属性 ----------------------------- 82 NSDictionary *attrDic = [self.fileManager attributesOfItemAtPath:test1Path error:nil]; 83 NSLog(@"test1文件属性信息:%@",attrDic); 84 85 // 获取文件创建时间 86 NSDate *date = [attrDic objectForKey:NSFileCreationDate]; 87 88 // 修改创建时间无效 89 NSMutableDictionary *changeAttrDic = [NSMutableDictionary dictionaryWithDictionary:attrDic]; 90 // [changeAttrDic removeAllObjects];// 想要修改文件的创建时间,需先删除以前的文本属性,否则修改无效 91 [changeAttrDic setObject:[NSDate dateWithTimeIntervalSinceNow:600] forKey:NSFileCreationDate]; 92 [self.fileManager setAttributes:changeAttrDic ofItemAtPath:test1Path error:nil]; 93 attrDic = [self.fileManager attributesOfItemAtPath:test1Path error:nil]; 94 NSLog(@"更改创建时间:%@",attrDic); 95 96 // 获得子文件或文件夹的名字 97 NSArray *subArray = [self.fileManager subpathsAtPath:filePath]; 98 NSLog(@"subArray===%@",subArray); 99 } 100 101 @end
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES) 方法参数说明
NSHomeDirectory 沙盒主路径
NSDocumentDirectory Document文件夹
NSLibraryDirectory Library文件夹
NSCachesDirectory Caches文件夹
NSTemporaryDirectory tmp文件夹
▶ 将简单对象写入文件
简单对象只能是 NSString、NSArray、NSDictionary、NSData 四种对象
1 #import "ViewController.h" 2 @interface ViewController () 3 @property(nonatomic,strong) NSFileManager *fileManager; // 文件管理器 4 5 @end 6 7 @implementation ViewController 8 9 - (void)viewDidLoad{ 10 [super viewDidLoad]; 11 } 12 13 14 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 15 16 // 获取所有 documents 的文件夹 17 NSArray *documentList = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES); 18 NSLog(@"documentList===%@",documentList); 19 // 将最后一个作为文件的存储目录 20 NSString *documentPath = documentList.lastObject; 21 NSLog(@"%@",documentPath); 22 23 //--------------将 字符串对象 写入文件------------ 24 // 构造字符串文件的存储路径 25 NSString *strPath = [documentPath stringByAppendingString:@"/text.txt"]; 26 NSString *foo_str = @"this for test"; 27 [foo_str writeToFile:strPath atomically:YES encoding:NSUTF8StringEncoding error:nil]; 28 // 读取 29 NSLog(@"%@",[[NSString string] initWithContentsOfFile:strPath encoding:NSUTF8StringEncoding error:nil]) ; 30 31 //--------------将 数组对象 写入文件-------------- 32 NSString *arrayPath = [documentPath stringByAppendingString:@"/array.plist"]; 33 NSArray *foo_array = @[@"1",@"2",@"3",@"4",@"5"]; 34 [foo_array writeToFile:arrayPath atomically:YES]; 35 NSLog(@"%@",[[NSArray array] initWithContentsOfFile:arrayPath]); 36 37 //--------------将 字典对象 写入文件-------------- 38 NSString *dicPath = [documentPath stringByAppendingString:@"/dic.plist"]; 39 NSDictionary *foo_dic = @{@"1":@"f",@"2":@"s",@"3":@"t"}; 40 [foo_dic writeToFile:dicPath atomically:YES]; 41 NSLog(@"%@",[[NSDictionary dictionary] initWithContentsOfFile:dicPath]); 42 43 // //--------------- 将 Data对象 写入文件 --------------- 44 // // Data 对象写入文件:二进制对象可能存储的是图像、字符串等等 45 // NSString *dataPath = [documentPath stringByAppendingString:@"/data.da"]; 46 // [foo_data writeToFile:dataPath atomically:YES]; 47 } 48 49 @end
▶ 将复杂对象写入文件
复杂对象无法通过 writeToFile: 方法进行数据持久化,只能通过先将复杂对象转化成 NSData,然后通过 writeToFile: 写入文件后保存
归档:将复杂对象转换成 NSData 数据
解档(反归档),就是将 NSData 数据转换成复杂对象
一个类如果要进行归档:必须接受 NSCoding 协议且实现协议中必须实现的方法!下面我们对 Person 进行归档/解档
// - Person.h
#import <Foundation/Foundation.h> @interface Person : NSObject<NSCoding>// 接受 NSCoding协议 @property(nonatomic,copy)NSString *name; @property(nonatomic,copy)NSString *sex; @property(nonatomic,assign)NSInteger age; @end
// - Person.m
1 #import "Person.h" 2 #define NAME_KEY @"name"// 解压数据需要的参数 3 #define SEX_KEY @"sex" 4 #define AGE_KEY @"age" 5 @implementation Person 6 7 // 归档时自动调用 8 - (void)encodeWithCoder:(NSCoder *)aCoder{ 9 10 // 把每一个实例变量转化成字节流 11 [aCoder encodeObject:self.name forKey:NAME_KEY]; 12 [aCoder encodeObject:self.sex forKey:SEX_KEY]; 13 [aCoder encodeInteger:self.age forKey:AGE_KEY]; 14 } 15 16 // 解档时自动调用 17 - (id)initWithCoder:(NSCoder *)aDecoder{ 18 self = [super init]; 19 if (self) { 20 self.name = [aDecoder decodeObjectForKey:NAME_KEY]; 21 self.sex = [aDecoder decodeObjectForKey:SEX_KEY]; 22 self.age = [aDecoder decodeIntegerForKey:AGE_KEY]; 23 } 24 return self; 25 } 26 27 28 @end
// - ViewController.m
1 #import "ViewController.h" 2 #import "Person.h" 3 #define SCREEN_BOUNDS [[UIScreen mainScreen] bounds] 4 #define SCREEN_SIZE SCREEN_BOUNDS.size 5 #define SCREEN_HEIGHT SCREEN_SIZE.height 6 #define SCREEN_WIDTH SCREEN_SIZE.width 7 @interface ViewController () 8 9 @property (strong, nonatomic)UITextField *nameTF; 10 @property (strong, nonatomic)UITextField *ageTF; 11 @property (strong, nonatomic)UITextField *sexTF; 12 @property (strong, nonatomic)UITextField *showInfoTF; // 解档后文本 13 14 @property (strong, nonatomic)UIButton *achiverButton; // 归档Btn 15 @property (strong, nonatomic)UIButton *unAchiverButton;// 解档Btn 16 17 @property(nonatomic,strong)NSMutableArray *dataArray; 18 19 @property (strong, nonatomic)NSKeyedArchiver *keyedArchiver; // 归档器 20 @property (strong, nonatomic)NSKeyedUnarchiver *keyedUnarchiver; // 解档器 21 22 @end 23 24 @implementation ViewController 25 26 - (void)viewDidLoad { 27 [super viewDidLoad]; 28 self.view.backgroundColor = [UIColor cyanColor]; 29 30 // 姓名 31 _nameTF = [[UITextField alloc] initWithFrame:CGRectMake(60, 90, SCREEN_WIDTH-120, 40)]; 32 _nameTF.placeholder = @"姓名"; 33 _nameTF.backgroundColor = [UIColor whiteColor]; 34 [self.view addSubview:_nameTF]; 35 36 // 性别 37 _sexTF = [[UITextField alloc] initWithFrame:CGRectMake(60, 170, SCREEN_WIDTH-120, 40)]; 38 _sexTF.backgroundColor = [UIColor whiteColor]; 39 _sexTF.placeholder = @"性别"; 40 [self.view addSubview:_sexTF]; 41 42 // 年龄 43 _ageTF = [[UITextField alloc] initWithFrame:CGRectMake(60, 250, SCREEN_WIDTH-120, 40)]; 44 _ageTF.backgroundColor = [UIColor whiteColor]; 45 _ageTF.placeholder = @"年龄"; 46 [self.view addSubview:_ageTF]; 47 48 // 归档 49 _achiverButton = [UIButton buttonWithType:UIButtonTypeCustom]; 50 [_achiverButton setBackgroundColor:[UIColor redColor]]; 51 [_achiverButton setTitle:@"归档" forState:UIControlStateNormal]; 52 [_achiverButton addTarget:self action:@selector(MakeSomething:) forControlEvents:UIControlEventTouchUpInside]; 53 _achiverButton.frame = CGRectMake((SCREEN_WIDTH-50)*0.5, 340, 50, 50); 54 _achiverButton.layer.masksToBounds = YES; 55 _achiverButton.layer.cornerRadius = 25.0f; 56 [self.view addSubview:_achiverButton]; 57 58 // 解档 59 _unAchiverButton = [UIButton buttonWithType:UIButtonTypeCustom]; 60 [_unAchiverButton setBackgroundColor:[UIColor redColor]]; 61 [_unAchiverButton setTitle:@"解档" forState:UIControlStateNormal]; 62 [_unAchiverButton addTarget:self action:@selector(MakeSomething:) forControlEvents:UIControlEventTouchUpInside]; 63 _unAchiverButton.frame = CGRectMake((SCREEN_WIDTH-100)*0.5, 420, 100, 50); 64 _unAchiverButton.layer.masksToBounds = YES; 65 _unAchiverButton.layer.cornerRadius = 25.0f; 66 [self.view addSubview:_unAchiverButton]; 67 68 // 显示解档信息 69 _showInfoTF = [[UITextField alloc] initWithFrame:CGRectMake(20, 520, SCREEN_WIDTH-40, 40)]; 70 _showInfoTF.backgroundColor = [UIColor whiteColor]; 71 [self.view addSubview:_showInfoTF]; 72 } 73 74 - (NSKeyedUnarchiver *)keyedUnarchiver{ 75 76 if(!_keyedUnarchiver){ 77 _keyedUnarchiver = [NSKeyedUnarchiver alloc]; 78 } 79 return _keyedUnarchiver; 80 } 81 82 83 - (NSKeyedArchiver *)keyedArchiver{ 84 85 if(!_keyedArchiver){ 86 _keyedArchiver = [NSKeyedArchiver alloc]; 87 } 88 return _keyedArchiver; 89 } 90 // 获取文件路径 91 - (NSString *)personFilePath{ 92 93 NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; 94 NSString *filePath = [documents stringByAppendingPathComponent:@"PersonFile"]; 95 NSLog(@"__%@__",filePath); 96 return filePath; 97 } 98 99 // 键盘回收 100 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 101 [self.view endEditing:YES]; 102 } 103 104 // 解档/归档 105 -(void)MakeSomething:(UIButton *)bt{ 106 107 // 归档 108 if (bt == _achiverButton) { 109 Person *person = [[Person alloc] init]; 110 person.name = _nameTF.text;// 进行赋值 111 person.sex = _sexTF.text; 112 person.age = [_ageTF.text integerValue]; 113 114 NSMutableData *data = [NSMutableData dataWithCapacity:1]; 115 NSKeyedArchiver *psARCH = [self.keyedArchiver initForWritingWithMutableData:data]; 116 [psARCH encodeObject:person forKey:@"personKey"]; 117 // 结束归档 118 [psARCH finishEncoding]; 119 // 写入磁盘 120 [data writeToFile:[self personFilePath] atomically:YES]; 121 122 // 解档 123 }else{ 124 125 NSData *data = [NSData dataWithContentsOfFile:[self personFilePath]]; 126 // 根据 data 生成解档器 127 NSKeyedUnarchiver *psUNCH = [self.keyedUnarchiver initForReadingWithData:data]; 128 // 根据 key 值从 data 中解档出 person对象 129 Person *person = [psUNCH decodeObjectForKey:@"personKey"]; 130 _showInfoTF.text = [NSString stringWithFormat:@"%@,%@,%ld",person.name,person.sex,person.age]; 131 132 } 133 } 134 135 @end
运行效果:将 Person 的姓名、性别、年龄归档后,通过点击解档按钮将信息展示在文本框中

浙公网安备 33010602011771号