这篇学习目标:
- 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。
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功能就可以了。它们的任务是把数据写到数据缓存,最后持久保存到磁盘中。
实现很简单,第一步:
1 @interface CustomData : NSObject2 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 管理文件,在此简单列出一些常见方法:
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中测试,所以在此只提一些概念,具体操作,后面有一篇完整的博文详细来实战,请大家期待。
明天是清明节,我有好多年没有在家里过清明节,也没有机会扫墓,按我老家说是上坟,突然就想起我爷爷与外婆,祝他们在天国过得更好。
明天开车回老家,想家。