今天被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
{
}
写上去就行了,不用去管有没有是不是第一次运行,因为机器默认不存在就再生成一份。
4、一般的话,在plist里面分层,容器都用NSMutableArray或者NSArray来做,至于NSDictionary行不行目前还没实验过。而且基本数据类型最后都用NSString比较保险。
创建与删除: //创建文件管理器 NSFileManager *fileManager = [NSFileManager defaultManager]; //获取路径 //参数NSDocumentDirectory要获取那种路径 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0];//去处需要的路径 //更改到待操作的目录下 [fileManager changeCurrentDirectoryPa th:[documentsDirectory stringByExpandingTildeIn Path]]; //创建文件fileName文件名称,contents文件的内容,如果开始没有内容可以设置为nil,attributes文件的属性,初始为nil [fileManager createFileAtPath:@"fileName" contents:nil attributes:nil]; //删除待删除的文件 [fileManager removeItemAtPath:@"createdNewFile" error:nil]; 写入数据: //获取文件路径 NSString *path = [documentsDirectory stringByAppendingPathCom ponent:@"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
NSMutableArray
写入plist的代码:
就这么简单。
//[array insertObject:@"hello" atIndex:2];//在第三个数后添加一个hello
//[array removeLastObject];//删掉最后一个
//[array count];
还有很多函数提供选择:
- (void)insertObjects:(NSArray
- (void)removeObjectsAtIndexes:(NSIndexSet
- (void)replaceObjectsAtIndexes:(NSIndexSet
- (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
- (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2;
- (void)removeAllObjects;
- (void)removeObject:(id)anObject inRange:(NSRange)range;
- (void)removeObject:(id)anObject;
- (void)removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range;
- (void)removeObjectIdenticalTo:(id)anObject;
- (void)removeObjectsFromIndices
- (void)removeObjectsInArray:(NSArray
- (void)removeObjectsInRange:(NSRange)range;
- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray
- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray
- (void)setArray:(NSArray
- (void)sortUsingFunction:(NSInteger
- (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”的文件夹下。访问路径的代码如下:
执行结果是 Support/iPhone Simulator/5.0/Applications/3B5DBF75-18D2-43EA-B26F-7FEDECAFDC92/Documents
可以看到系统文件名ID和上面的一样。
综 上所述。当有数据为只读的时候,应该放到app应用里的plist里,当数据要做修改,应该放到documents里。比如游戏里的物品数据,这种不能被 修改的放到app里,而玩家的合成装备应该在documents里手动创建一个plist来存储。那么首要问题就是要判断,documents里是否已有 数据。
NSArray *doc = NSSearchPathForDirectori
NSString *docPath = [ doc objectAtIndex:0 ];
if( [[NSFileManager defaultManager] fileExistsAtPath:[docPathstringByAppending
}
读取:
NSArray *doc = NSSearchPathForDirectori
NSString *docPath = [ doc objectAtIndex:0 ]; // 字典集合。
NSDictionary *dic = [ NSDictionary dictionaryWithContentsOf
NSString *content =
NSArray *array = [ content componentsSeparatedByStr
content里就是“Score”里所存储的数据,array是将content里的数据按“,”拆分,仅将两个“,”之间的数据保存。
写入:一定要注意,必须创建一个新的NSMutableDictionary
// 用来覆盖原始数据的新dic
NSMutableDictionary *newDic = [ [ NSMutableDictionary alloc ] init ];
//
NSString *newScore = @"100,200,300";
//
[ newDic setValue:newScore forKey:@"Score" ];
//
NSArray *doc = NSSearchPathForDirectori
NSString *docPath = [ doc objectAtIndex:0 ];
[ newDic writeToFile:[docPath stringByAppendingPathCom
I'm trying to replace my current preference file with the preference file the user just downloaded into Documents. I'm guessing it might have to do with the preference file still being open, but resetStandardUserDefault
-(void)movePrefsFile
{
NSArray *paths = NSSearchPathForDirectori esInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *ttFolder = [paths objectAtIndex: 0];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths2 = NSSearchPathForDirectori esInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *ttFolder2 = [paths2 objectAtIndex: 0];
NSError *error=nil;
NSString *prefsPath, *newPrefsPath;
[NSUserDefaults resetStandardUserDefault s];
prefsPath = [ttFolder stringByAppendingPathCom ponent:@"com.catamount.pocketmoney.plist"];
newPrefsPath = [ttFolder2 stringByAppendingPathCom ponent:@"/Preferences/com.catamount.pocketmoney.plist"];
NSLog(@"prefsPath=%@",prefsPath);
NSLog(@"newPrefsPath=%@",newPrefsPath);
BOOL success = [fileManager removeItemAtPath:newPrefsPath error:&error];
if (!success)
{
NSLog(@"couldn't delete prefs plist file: %@",error);
}
success = [fileManager moveItemAtPath:prefsPath toPath:newPrefsPath error:&error];
if (!success)
{
NSLog(@"couldn't move in new prefs plist file");
}
[NSUserDefaults standardUserDefaults];
}
removeItemAtPath:error: returns an error:
Error Domain=NSCocoaErrorDomain Code=4 UserInfo=0x18d860 "Operation could not be completed. (Cocoa error 4.)"