这篇学习目标:
- plist文件读写
- 对象序列化与反序列化
- 本地文件的读写
- iCloud存储的API
引言
在做iOS开发时,经常用到到plist文件, 那plist文件是什么呢? 它全名是:Property List,属性列表文件,它是一种用来存储串行化后的对象的文件。属性列表文件的扩展名为.plist ,因此通常被称为 plist文件。文件是xml格式的。plist文件通常用于储存用户设置,也可以用于存储捆绑的信息。
有时需要把某个对象进行序列化后存储起来,也需要反序列化读取数据。
关于Core Data框架,本篇不说,如果有机会,后面有博客详细说明。
一、plist 文件读取与写入
plist 文件创建、写入内容。内容比较简单,我直接用代码加注释的方式来表达,亲,OK啦!
plist 支持的数据类型有 Array、Dictionary、Boolean、Data、Date、Number、String这些类型,其他的类型支持,所以一般需要转化一下再存。记住plist本质上是一个XML。
PList 文件创建、读、写入等操作
1 - (void)viewDidLoad
2 {
3 [super viewDidLoad];
4
5 //获取应用程序沙盒的Documents目录
6 NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
7 NSString *p = [paths objectAtIndex:0];
8 NSString *filename = [p stringByAppendingPathComponent:@"Sample.plist"];
9
10 // 创建文件管理对象
11 NSFileManager *fm = [NSFileManager defaultManager];
12 if ([fm fileExistsAtPath:filename]) // 判断文件是否存在
13 {
14 NSError *error;
15 [fm removeItemAtPath:filename error:&error]; // 删除旧文件
16 NSLog(@"%@",error);
17 }
18
19 // 创建文件零字节的文件
20 [fm createFileAtPath:filename contents:nil attributes:nil];
21
22 // 创建一个Dictionary (注:NSMutableDictionary同NSDictionary,主要区分:可变与不可变)
23 //NSDictionary *dict = [NSDictionary dictionaryWithObject:@"一条龙" forKey:@"Year"];
24 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
25
26 //数据类型可以是:Array、Dictionary、Boolean、Data、Date、Number、String
27
28 // 新增一条 String 数据
29 NSString *name = @"周星驰(Stephen Chow)";
30 [dict setObject:name forKey:@"姓名"];
31
32 // 新增一条 Number 数据
33 NSNumber *year = [NSNumber numberWithInt:100];
34 [dict setObject:year forKey:@"影片产量"];
35
36 // 新增一条 Date 数据
37 NSDate *date = [NSDate date]; //获取当前时间
38 [dict setObject:date forKey:@"时间"];
39
40 // 新增一条 Data 数据
41 // NSData类提供了一种简单的方式,它用来设置缓冲区、将文件的内容读入缓冲区,或将缓冲区的内容写到一个文件。 对于32位应用程序,NSData缓存区最多可以存储2GB的数据。
42 // 还可以存放图片内容,请你们自己测试一下
43 NSString *d = @"NSData类提供了一种简单的方式,它用来设置缓冲区、将文件的内容读入缓冲区,或将缓冲区的内容写到一个文件。 对于32位应用程序,NSData缓存区最多可以存储2GB的数据。";
44 NSData *data = [d dataUsingEncoding:NSUTF8StringEncoding];
45 [dict setObject:data forKey:@"简介"];
46
47 // 新增一条 BOOL 数据,Obj-C布尔型是以数字存储的
48 [dict setObject:[NSNumber numberWithBool:YES] forKey:@"搞笑"];
49
50 // 新增一条 Array 数据
51 NSMutableArray *array = [[NSMutableArray alloc] init];
52 [array addObject:@"西游·降魔篇 ( 2012)"];
53 [array addObject:@"长江7号 ( 2007)"];
54 [array addObject:@"功夫 ( 2004)"];
55 [array addObject:@"少林足球 ( 2001)"];
56 [array addObject:@"千王之王 ( 1999)"];
57 [array addObject:@"喜剧之王 ( 1999)"];
58 [array addObject:@"行运一条龙 ( 1998)"];
59 [array addObject:@"算死草 ( 1997)"];
60 [array addObject:@"97家有喜事 ( 1997)"];
61 [array addObject:@"食神 ( 1996)"];
62 [array addObject:@"大内密探零零发 ( 1996)"];
63 [array addObject:@"百变星君 ( 1995)"];
64 [array addObject:@"回魂夜 ( 1995)"];
65 [array addObject:@"大话西游之月光宝盒 ( 1995)"];
66 [array addObject:@"国产凌凌漆 ( 1994)"];
67 [array addObject:@"九品芝麻官之白面包青天 ( 1994)"];
68 [array addObject:@"破坏之王 ( 1994)"];
69 [array addObject:@"济公 ( 1993)"];
70 [array addObject:@"唐伯虎点秋香 ( 1993)"];
71 [array addObject:@"逃学威龙3龙过鸡年 ( 1993)"];
72 [array addObject:@"审死官 ( 1993)"];
73
74 [dict setValue:array forKey:@"作品集"];
75
76 // 新增 Dictionary 数据,这里就偷个懒,把前的字典内容添加到一个对象中,然后再添加
77 NSDictionary *test = [NSDictionary dictionaryWithDictionary:dict];
78 [dict setValue:test forKey:@"字典"];
79
80 // 写入文件
81 [dict writeToFile:filename atomically:YES];
82
83 // 读取数据到对象中
84 NSMutableDictionary *readDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filename ];
85
86 // 读到指定的值
87 NSLog(@"key '姓名:' is %@",[readDict objectForKey:@"姓名"]);
88
89 // 输出全部内容
90 NSLog(@"data is:%@",readDict);
91
92 NSLog(@"File Name:%@",filename);
93 }
到模拟器的目录下查看操作结果:
具体文件所在位置,可以通过NSLog打印出来的地址,进行查找
如果找不到路径,可能是MAC系统隐藏部分文件,可以通过以下命令显示出来:
显示或隐藏文件
1 显示Mac隐藏文件的命令:defaults write com.apple.finder AppleShowAllFiles -bool true
2 隐藏Mac隐藏文件的命令:defaults write com.apple.finder AppleShowAllFiles -bool false
3
4 或者
5
6 显示Mac隐藏文件的命令:defaults write com.apple.finder AppleShowAllFiles YES
7 隐藏Mac隐藏文件的命令:defaults write com.apple.finder AppleShowAllFiles NO
重启Finder:鼠标单击窗口左上角的苹果标志-->强制退出-->Finder-->重新启动,就可以找到生成的文件
二、对象序列化与反序列化
为什么要进行对象的序列化操作呢?将内存中的对象实例保存成binary到 磁盘,并且可以逆向这个过程用来保存各操作状态或恢复时数据。亲,大家都懂的,在这就不多说了。
- 序列化对象:NSKeyedArchiver
- 反序列化对象:NSKeyedUnarchiver
具体代码如下:
序列化与反序列化
1 - (void)viewDidLoad
2 {
3 // 创建一个数组
4 NSMutableArray *array = [[NSMutableArray alloc] init];
5 [array addObject:@"西游·降魔篇 ( 2012)"];
6 [array addObject:@"长江7号 ( 2007)"];
7 [array addObject:@"功夫 ( 2004)"];
8 [array addObject:@"少林足球 ( 2001)"];
9 [array addObject:@"千王之王 ( 1999)"];
10 [array addObject:@"喜剧之王 ( 1999)"];
11 [array addObject:@"行运一条龙 ( 1998)"];
12
13 // 获取应用程序沙盒的Documents目录
14 NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
15 NSString *p = [paths objectAtIndex:0];
16 NSString *filename = [p stringByAppendingPathComponent:@"State.data"];
17
18 // 序列化并保存到文件中
19 [NSKeyedArchiver archiveRootObject:array toFile:filename];
20
21
22 // 反序列化并加载数据
23 NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithFile: filename];
24 NSLog(@"str:%@", [arr objectAtIndex:0]);
25 NSLog(@"str:%@", [arr objectAtIndex:1]);
26 }
是不是非常简单,同样会生成一个二进制的文件存放在Documents目录下,同学们,自己找一找,这里就没图了。
对一些特殊需求的同学会问:有没有办法可以把自己设计的类进行序列化,并且加密?答案是:肯定可以。只要实现NSCoding协议就可以。
NSCoding是什么呢?
NSCoding是一个可以由你自行实现的协议,通过扩展你的数据类(data class)来支持encode和decode功能就可以了。它们的任务是把数据写到数据缓存,最后持久保存到磁盘中。
实现很简单,第一步:
继承协议NSCoding
1 @interface CustomData : NSObject<NSCoding>
2
3 @property (nonatomic,retain) NSString *title;
4 @property (nonatomic,retain) NSString *content;
5
6 -(id)initWithTitle:(NSString *)tit Content:(NSString *)cont;
第二步,具体实现,也很简单,直接在代码中注解了
具体实现协议
1 @implementation CustomData
2
3 @synthesize title;
4 @synthesize content;
5
6 -(id)initWithTitle:(NSString *)tit Content:(NSString *)cont
7 {
8 if ((self = [super init])) {
9 title = [tit copy];
10 content = [cont copy];
11 }
12 return self;
13 }
14
15
16 #pragma mark 实现 NSCoding 协议
17 #define keyTitle @"Title"
18 #define keyContent @"Content"
19
20 // 编码
21 -(void)encodeWithCoder:(NSCoder *)aCoder{
22 [aCoder encodeObject:title forKey:keyTitle];
23 [aCoder encodeObject:content forKey:keyContent];
24 }
25
26 // 解码
27 -(id)initWithCoder:(NSCoder *)aDecoder
28 {
29 NSString *t = [aDecoder decodeObjectForKey:keyTitle];
30 NSString *c = [aDecoder decodeObjectForKey:keyContent];
31 return [self initWithTitle:t Content:c];
32 }
33
34 @end
第三步,调用自定义类
调用自定义类
1 - (void)viewDidLoad
2 {
3 [super viewDidLoad];
4 //获取应用程序沙盒的Documents目录
5 NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
6 NSString *p = [paths objectAtIndex:0];
7 NSString *filename = [p stringByAppendingPathComponent:@"encode.data"];
8
9 // 创建文件管理对象
10 NSFileManager *fm = [NSFileManager defaultManager];
11 if ([fm fileExistsAtPath:filename]) // 判断文件是否存在
12 {
13 NSError *error;
14 [fm removeItemAtPath:filename error:&error]; // 删除旧文件
15 NSLog(@"%@",error);
16 }
17 // 创建自定义对象
18 CustomData *d = [[CustomData alloc] initWithTitle:@"标题" Content:@"内容"];
19
20 // 序列化保存成文件
21 [NSKeyedArchiver archiveRootObject:d toFile:filename];
22
23 // 反序列化读取文件操作
24 CustomData *ud = [NSKeyedUnarchiver unarchiveObjectWithFile: filename];
25 NSLog(@"Title is:%@ ",ud.title);
26 NSLog(@"Title is:%@ ",ud.content);
27 }
三、本地文件的操作
出于安全的目的,应用程序只能将自己的数据和偏好设置写入到几个特定的位置上。当应用程序被安装到设备上时,系统会为其创建一个家目录。
- /Documents/ 您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据或其它应该定期备份的信息。iTunes会备份这个目录的内容。
- /Library/Preferences 这个目录包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类或CFPreferences API来取得和设置应用程序的偏好。iTunes会备份这个目录的内容。
- /Library/Caches 这个目录用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。您的应用程序通常需要负责添加和删除这些文件,但在对设备进行完全恢复的过程中,iTunes会删除这些文件。
- /tmp/ 这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。当您的应用程序不再需要这些临时文件时,应该将其从这个目录中删除(系统也可能在应用程序不运行的时候清理留在这个目录下的文件)。
可以用NSSearchPathForDirectoriesInDomains和NSTemporaryDirectory函数来取得Documents、Caches、和tmp目录的准确路径。
具体通过以下代码获取目录:
获取用户目录
1 - (void)viewDidLoad
2 {
3 [super viewDidLoad];
4 //获取应用程序沙盒的目录
5
6 // 获取 主目录 方法
7 NSString *home = NSHomeDirectory();
8 NSLog(@"Home: %@",home);
9
10 // 获取 Documents 方法
11 NSArray *document = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
12 NSLog(@"document: %@",[document objectAtIndex:0]);
13
14 // 获取 PreferencePanes 方法
15 NSArray *preferences = NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSUserDomainMask, YES);
16 NSLog(@"preferences: %@",[preferences objectAtIndex:0]);
17
18 // 获取 Caches 方法
19 NSArray *caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
20 NSLog(@"caches: %@",[caches objectAtIndex:0]);
21
22 // 获取 tmp 方法
23 NSString *tmp = NSTemporaryDirectory();
24 NSLog(@"tmp: %@",tmp);
25 }
有几点建议:
- 读写用户偏好设置时,请使用NSUserDefaults类进行操作,后继有博文来详述。
- 应用程序的程序包中包含声音、图像、或其它资源,则应该使用NSBundle封装类型来装载资源。
- 仿真器上返回的路径与真实设备返回的路径,有可能不一样。
使用 NSFileManager 管理文件,在此简单列出一些常见方法:
NSFileManager 文件管理
1 -(void)viewDidLoad
2 {
3 NSError *error;
4 //获取应用程序沙盒的Documents目录
5 NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
6 NSString *p = [paths objectAtIndex:0];
7 NSString *file1 = [p stringByAppendingPathComponent:@"Sample_1.txt"];
8 NSString *file2 = [p stringByAppendingPathComponent:@"Sample_2.txt"];
9 NSString *file3 = [p stringByAppendingPathComponent:@"Sample_3.txt"];
10 NSString *file4 = [p stringByAppendingPathComponent:@"Sample_4.txt"];
11
12 // 从网站上获取内容
13 NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://www.chinapcc.com"]];
14
15 // 创建文件管理对象
16 NSFileManager *fm = [NSFileManager defaultManager];
17 if (![fm fileExistsAtPath:file1]) // 判断文件是否存在
18 {
19 // 文件不存在,将创建
20 [fm createFileAtPath:file1 contents:data attributes:nil];
21 }
22
23 //输出文件内容
24 NSLog(@"%@",[NSString stringWithContentsOfFile:file1 encoding:NSUTF8StringEncoding error:&error]);
25
26 // 读取出文件1的内容
27 NSData *data2 =[fm contentsAtPath:file1];
28 if ([fm fileExistsAtPath:file2]) // 判断文件是否存在
29 {
30 //删除文件file2
31 [fm removeItemAtPath:file2 error:&error];
32 }
33
34 // 创建文件2
35 [fm createFileAtPath:file2 contents:data2 attributes:nil];
36
37 // 文件1 复制成 文件2 对文件复制
38 [fm copyItemAtPath:file1 toPath:file3 error:&error];
39
40 // 文件3 改名为 文件4 对文件进行移动,在同一目录,就可理解为改名
41 [fm moveItemAtPath:file3 toPath:file4 error:&error];
42
43 //下面是目录枚举 这种方法会递归
44 NSDirectoryEnumerator *dir= [fm enumeratorAtPath:NSHomeDirectory()];
45 NSString *path=[NSString new];
46 while ((path=[dir nextObject])!=nil) {
47 NSLog(@"%@",path);
48 }
49 }
三、iCloud存储的API
- iCloud document storage,利用iCloud存储用户文件,比如保存一些用户在使用应用时生成的文件,如数据库文件等;
- iCloud key-value data storage,利用iCloud存储键值对,主要是保存一些程序的设置信息,一般只允许存储几十K大小。
应用主目录下的Document目录和Library目录(除了该目录下的Caches)的文件都会被自动的备份到iCloud,因此,可以把较大的临时文件或随时可以重建的文件放在Library\Caches目录下,就可以帮用户节省iCloud空间,提供用户体验。另外,相信大家都关心,应用存放在iCloud上如何保证安全呢,不用的应用保存的数据是否能够有效隔离?这一点,iCloud引入了iOS下沙盒的概念,即每一个应用在iCloud上都有一个沙盒,访问的范围就被限制在这里面,从而保证数据安全和隔离。
iCloud目前只能在真实设备中测试,不能在Simulator中测试,所以在此只提一些概念,具体操作,后面有一篇完整的博文详细来实战,请大家期待。