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 的姓名、性别、年龄归档后,通过点击解档按钮将信息展示在文本框中

 

 

posted on 2017-05-02 15:24  低头捡石頭  阅读(66)  评论(0)    收藏  举报

导航