plist是个好东西:
1、不要太看得起plist,他没你想像的那么强大,不是跟一个万能仓库一样, 什么东西都可以存取。一般说来,支持的数据类型有(NS省略)Dictionary、Array、Boolean、Data、Date、Number、 String这些类型,其他的类型支持,所以一般需要转化一下再存。我傻傻的以为它很厉害,放了一个View给它,企图把View放到Array里面包起 来,再把Array作为最外层的Dictionary的Value字段。。。。对,没错,一个View也是一个object,但是不要忘记,plist其实是一个XML文档。一个View你如何让他表达出来??所以,这样做的后果是,没报错,但是不会有值存进去
2、plist支持分层的数据。
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的写入是,你把你放在工程中的plist删掉。你要写入plist的时候, 如果发现没有该plist,其会帮新建该plist。别傻傻的认为自己建立一个plist,然后 运行程序的时候他会在你建的那plist里面多出几行数据,因为你修改的是应用中的 plist而非你本地的那个plist。 下面的plist里面存放这的是array数组 以下是显示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 沙盒问题
保存玩家数据,模拟器读写都可以,而真机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 ];
The following code works in the Simulator, but not on the device.
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
resetStandardUserDefaults ought to close it.
-(void)movePrefsFile { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *ttFolder = [paths objectAtIndex: 0]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSArray *paths2 = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); NSString *ttFolder2 = [paths2 objectAtIndex: 0]; NSError *error=nil; NSString *prefsPath, *newPrefsPath; [NSUserDefaults resetStandardUserDefaults]; prefsPath = [ttFolder stringByAppendingPathComponent:@"com.catamount.pocketmoney.plist"]; newPrefsPath = [ttFolder2 stringByAppendingPathComponent:@"/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.)"
Message was edited by: Hardy Macia
MacBookPro, Mac OS X (10.5.3), iPhone