今天被plist搞死了,项目需要,所以选择plist来存数据,但是今天各种尝试,处处碰壁,虽然浪费了时间,不过对与plist这个好东西的认识加深了不少:
1、不要太看得起plist,他没你想像的那么强大,不是跟一个万能仓库一样, 什么东西都可以存取。一般说来,支持的数据类型有(NS省略)Dictionary、Array、Boolean、Data、Date、Number、String这些类型,其他的类型支持,所以一般需要转化一下再存。我傻傻的以为它很厉害,放了一个View给它,企图把View放到Array里面包起来,再把Array作为最外层的Dictionary的Value字段。。。。对,没错,一个View也是一个object,但是不要忘记,plist其实是一个XML文档。一个View你如何让他表达出来??所以,这样做的后果是,没报错,但是不会有值存进去
2、plist支持分层的数据,比如看下图
ssss的value是一个array类型,里面有三个元素;最后一个元素也是一个数组,里面有两个元素。
3、plist最后会出现在程序的Documents文件夹下,与工程里面右键添加的那个东西是两码事。
- (NSString *) dataPath
{
NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)
objectAtIndex:0];
return [documentDir stringByAppendingPathComponent:@"data.plist"];
}
写上去就行了,不用去管有没有是不是第一次运行,因为机器默认不存在就再生成一份。
4、一般的话,在plist里面分层,容器都用NSMutableArray或者NSArray来做,至于NSDictionary行不行目前还没实验过。而且基本数据类型最后都用NSString比较保险。
创建与删除: //创建文件管理器 NSFileManager *fileManager = [NSFileManager defaultManager]; //获取路径 //参数NSDocumentDirectory要获取那种路径 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0];//去处需要的路径 //更改到待操作的目录下 [fileManager changeCurrentDirectoryPath:[documentsDirectory stringByExpandingTildeInPath]]; //创建文件fileName文件名称,contents文件的内容,如果开始没有内容可以设置为nil,attributes文件的属性,初始为nil [fileManager createFileAtPath:@"fileName" contents:nil attributes:nil]; //删除待删除的文件 [fileManager removeItemAtPath:@"createdNewFile" error:nil]; 写入数据: //获取文件路径 NSString *path = [documentsDirectory stringByAppendingPathComponent:@"fileName"]; //待写入的数据 NSString *temp = @”Hello friend”; int data0 = 100000; float data1 = 23.45f; //创建数据缓冲 NSMutableData *writer = [[NSMutableData alloc] init]; //将字符串添加到缓冲中 [writer appendData:[temp dataUsingEncoding:NSUTF8StringEncoding]]; //将其他数据添加到缓冲中 [writer appendBytes:&data0 length:sizeof(data0)]; [writer appendBytes:&data1 length:sizeof(data1)]; //将缓冲的数据写入到文件中 [writer writeToFile:path atomically:YES]; [writer release]; 读取数据: int gData0; float gData1; NSString *gData2; NSData *reader = [NSData dataWithContentsOfFile:path]; gData2 = [[NSString alloc] initWithData:[reader subdataWithRange:NSMakeRange(0, [temp length])] encoding:NSUTF8StringEncoding]; [reader getBytes:&gData0 range:NSMakeRange([temp length], sizeof(gData0))]; [reader getBytes:&gData2 range:NSMakeRange([temp length] + sizeof(gData0), sizeof(gData1))]; NSLog(@”gData0:%@ gData1:%i gData2:%f”, gData0, gData1, gData2); 读取工程中的文件: 读取数据时,要看待读取的文件原有的文件格式,是字节码还是文本,我经常需要重文件中读取字节码,所以我写的是读取字节文件的方式。 //用于存放数据的变量,因为是字节,所以是UInt8 UInt8 b = 0; //获取文件路径 NSString *path = [[NSBundle mainBundle] pathForResource:@”fileName” ofType:@”"]; //获取数据 NSData *reader = [NSData dataWithContentsOfFile:path]; //获取字节的个数 int length = [reader length]; NSLog(@”——->bytesLength:%d”, length); for(int i = 0; i < length; i++) { //读取数据 [reader getBytes:&b range:NSMakeRange(i, sizeof(b))]; NSLog(@”——–>data%d:%d”, i, b); }
弄了半天的plist,最无语的莫过于plist还分种类的。有字典型和数组型等。
NSString *path = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"];
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSLog(@"array:%@",[array objectAtIndex:0]);
写入plist的代码:
NSString *path1 = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"];
NSArray *array1 = [[NSArray alloc] initWithObjects:@"hello1",@"hello2",@"hello3",nil];
[array1 writeToFile:path1 atomically:YES];
就这么简单。
NSString *path = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"];
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSString *str=@"第六章——第三阶——第五页";
[array insertObject:str atIndex:[array count]]; //添加一行:
[array removeObjectsAtIndexes:2]; //删除第三行
[array replaceObjectsAtIndexes:2 withObjects:str;//修改第三行
[array writeToFile:path atomically:YES];
//[array insertObject:@"hello" atIndex:2];//在第三个数后添加一个hello
//[array removeLastObject];//删掉最后一个
//[array count]; //数组的总数
还有很多函数提供选择:
- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes;
- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectsAtIndexes:(NSIndexSet *)indexes withObjects:(NSArray *)objects;
- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
- (void)addObjectsFromArray:(NSArray *)otherArray;
- (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2;
- (void)removeAllObjects; //清空plist
- (void)removeObject:(id)anObject inRange:(NSRange)range;
- (void)removeObject:(id)anObject;
- (void)removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range;
- (void)removeObjectIdenticalTo:(id)anObject;
- (void)removeObjectsFromIndices:(NSUInteger *)indices numIndices:(NSUInteger)cntNS_DEPRECATED(10_0, 10_6, 2_0, 4_0);
- (void)removeObjectsInArray:(NSArray *)otherArray;
- (void)removeObjectsInRange:(NSRange)range;
- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray range:(NSRange)otherRange;
- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray;
- (void)setArray:(NSArray *)otherArray;
- (void)sortUsingFunction:(NSInteger (*)(id, id, void *))compare context:(void *)context;
- (void)sortUsingSelector:(SEL)comparator;
保存玩家数据,模拟器读写都可以,而真机plist文件只能读不能写,十分头大,弄球一天找到了问题所在。
按照网上比较有说服力的说法是:iOS程序执行的时候是在“沙盒”里执行。而沙盒里的数据不能写入,只能读取。
经过测试,当一个程序在执行的时候,比如叫 Test.app 的iOS程序,获得他的执行地址的代码是(比如找的是CFG.plist文件)
NSBundle *bundle = [ NSBundle mainBundle ];
NSString *filePath = [ bundle pathForResource:@"CFG" ofType:@"plist" ];
filePath打印出来的执行地址应该类似
Support/iPhone Simulator/5.0/Applications/3B5DBF75-18D2-43EA-B26F-7FEDECAFDC92/Test.app/CFG.plist
每 个应用程序都一个固定且唯一的ID(上面的3B5DBF75-18D2-43EA-B26F-7FEDECAFDC92),这个ID被作为iOS执行时的 一个用来修饰的文件夹,这样可以保证每个应用都是独立的,哪怕名字一样。而这个ID文件夹下有一系列实际存在的文件夹。而Test.app只是其中一个, 里面有实际的游戏数据。如果要想保存数据,那么应该将数据写入到一个叫做“Documents”的文件夹下。访问路径的代码如下:
NSArray *doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docPath = [ doc objectAtIndex:0 ];
执行结果是 Support/iPhone Simulator/5.0/Applications/3B5DBF75-18D2-43EA-B26F-7FEDECAFDC92/Documents
可以看到系统文件名ID和上面的一样。
综 上所述。当有数据为只读的时候,应该放到app应用里的plist里,当数据要做修改,应该放到documents里。比如游戏里的物品数据,这种不能被 修改的放到app里,而玩家的合成装备应该在documents里手动创建一个plist来存储。那么首要问题就是要判断,documents里是否已有 数据。
NSArray *doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [ doc objectAtIndex:0 ];
if( [[NSFileManager defaultManager] fileExistsAtPath:[docPathstringByAppendingPathComponent:@"Score.plist"] ]==NO ) {
// ============================== 写入plist初始化数据(最后有,先说读取)
}
读取:
NSArray *doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [ doc objectAtIndex:0 ]; // 字典集合。
NSDictionary *dic = [ NSDictionary dictionaryWithContentsOfFile:[docPathstringByAppendingPathComponent:@"Score.plist"] ]; // 解析数据
NSString *content = [ dic objectForKey:@"Score" ];
NSArray *array = [ content componentsSeparatedByString:@","];
content里就是“Score”里所存储的数据,array是将content里的数据按“,”拆分,仅将两个“,”之间的数据保存。
写入:一定要注意,必须创建一个新的NSMutableDictionary
// 用来覆盖原始数据的新dic
NSMutableDictionary *newDic = [ [ NSMutableDictionary alloc ] init ];
// 新数据
NSString *newScore = @"100,200,300";
// 将新的dic里的“Score”项里的数据写为“newScore”
[ newDic setValue:newScore forKey:@"Score" ];
// 将 newDic 保存至docPath+“Score.plist”文件里,也就是覆盖原来的文件
NSArray *doc = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [ doc objectAtIndex:0 ];
[ newDic writeToFile:[docPath stringByAppendingPathComponent:@"Score.plist"] atomically:YES ];