ios的文件加载和保存

最近,一直在看比较基础的知识,因为感觉自己需要整理一下最基层的东西,然后才能系统的结合起来,很多时候我们做项目都需要对文件加载和保存,我自己对已有知识整理了一下:

1、使用属性列表保存对象:

在Cocoa中,与一类名为属性列表的对象,常简称为plist。这些列表包含Cocoa知道如何操作的一组对象。具体来讲,Cocoa知道如何将它们保存到文件中并进行加载。属性列表类包括:NSArray,NSDictionary,NSString和NSData,以及它们的变体(Mutable)

  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  2. NSArray *array = [NSArray arrayWithObjects:@"First",
  3. @"second",@"third",@"fourth",@"fifth",nil];
  4. [array writeToFile:@"array.plist" atomically:YES];

2、编码对象

遗憾的是,无法总是将对象信息表示为属性列表类。如果能将所有对象都表示为数组字典,我们就没有必要使用自己的类了。所幸,Cocoa具备一种机制来将对象自身转化为某种格式并保存到磁盘中。对象可以将它们的实例变量和其它数据编码为数据块,然后保存到磁盘中。遗憾将这些数据块读到内存中,并且还能基于保存的数据创建新对象。这个过程称为编码和解码,或称为序列化和反序列化。

通过NSCoding协议,可以使用自己的对象实现相同功能,实现它的两个方法:

- (void)encodeWithCoder:(NSCoder *)aCoder;

- (id)initWithCoder:(NSCoder *)aDecoder;

NSCoder是一个抽象类,定义一些有用的方法来在对象与NSData之间来回转换。完全不需要创建新NSCoder,因为它事件上并无多大作用。但是我们实际上要使用NSCoder的一些具体子类来编码和解码对象。我们将使用其中两个子类NSKeyedArchiver和NSKeyedUnArchiver.

下面是一个例子:

头文件类BookObj.h的源码:

  1. //
  2. //  BookObj.h
  3. //
  4. #import <Cocoa/Cocoa.h>
  5. @interface BookObj:NSObject<NSCoding>{
  6. NSString *bookName;
  7. NSString *author;
  8. }
  9. @property (copy) NSString *bookName;
  10. @property (copy) NSString *author;
  11. -(id)initWithName:(NSString *)name
  12. author:(NSString *) au ;

实现类BookObj.m的源码:

  1. //
  2. //  BookObj.m
  3. //
  4. #import "BookObj.h"
  5. @implementation BookObj
  6. @synthesize bookName;
  7. @synthesize author;
  8. -(id)initWithName:(NSString *)name
  9. author:(NSString *) au{
  10. if (self = [super init]) {
  11. self.bookName = name;
  12. self.author = au;
  13. }
  14. return self;
  15. }
  16. - (void)encodeWithCoder:(NSCoder *)aCoder{
  17. [aCoder encodeObject:self.bookName forKey:@"bookName"];
  18. [aCoder encodeObject:self.author    forKey:@"author"];
  19. }
  20. - (id)initWithCoder:(NSCoder *)aDecoder{
  21. if (self =[super init]) {
  22. self.bookName = [aDecoder decodeObjectForKey:@"bookName"];
  23. self.author = [aDecoder decodeObjectForKey:@"author"];
  24. }
  25. return self;
  26. }
  27. int main(int argc ,const char *argv[]){
  28. BookObj *bookObj = [[BookObj alloc] initWithName:@"iPhone编程指南"
  29. author:@"David"];
  30. [NSKeyedArchiver archiveRootObject:bookObj toFile:@"bookObj.plist"];
  31. NSLog(@"Success to archive file bookObj.plist!");
  32. BookObj *bookOb =   [NSKeyedUnarchiver unarchiveObjectWithFile:@"bookObj.plist"];
  33. NSLog(@"The book name is :%@",bookOb.author);
  34. return 0;
  35. }
  36. @end

/==============================================================

下面镔哥具体介绍一下plist文件和NSCoding协议

在做iOS开发时,经常用到到plist文件,  那plist文件是什么呢? 它全名是:Property List,属性列表文件,它是一种用来存储串行化后的对象的文件。属性列表文件的扩展名为.plist ,因此通常被称为 plist文件。文件是xml格式的。

Plist文件通常用于储存用户设置,也可以用于存储捆绑的信息

1、创建项目Plistdemo

项目创建之后可以找到项目对应的plist文件,打开如下图所示:

在编辑器中显示类似与表格的形式,可以在plist上右键,用源码方式打开,就能看到plist文件的xml格式了。

2、创建plist文件。

按command +N快捷键创建,或者File —> New —> New File,选择Mac OS X下的Property List

创建plist文件名为plistdemo。

打开plistdemo文件,在空白出右键,右键选择Add row 添加数据,添加成功一条数据后,在这条数据上右键看到 value Type选择Dictionary。点加号添加这个Dictionary下的数据

添加完key之后在后面添加Value的值,添加手机号和年龄

创建完成之后用source code查看到plist文件是这样的:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3. <plist version="1.0">
  4. <dict>
  5. <key>jack</key>
  6. <dict>
  7. <key>phone_num</key>
  8. <string>13801111111</string>
  9. <key>age</key>
  10. <string>22</string>
  11. </dict>
  12. <key>tom</key>
  13. <dict>
  14. <key>phone_num</key>
  15. <string>13901111111</string>
  16. <key>age</key>
  17. <string>36</string>
  18. </dict>
  19. </dict>
  20. </plist>
  21. 3、读取plist文件的数据

    现在文件创建成功了,如何读取呢,实现代码如下:

    1. - (void)viewDidLoad
    2. {
    3. [super viewDidLoad];
    4. //读取plist
    5. NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"plistdemo" ofType:@"plist"];
    6. NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
    7. NSLog(@"%@", data);//直接打印数据。
    8. }

    打印出来的结果:

    [cpp] view
    plain
    copy

    1. PlistDemo[6822:f803] {
    2. jack =     {
    3. age = 22;
    4. "phone_num" = 13801111111;
    5. };
    6. tom =     {
    7. age = 36;
    8. "phone_num" = 13901111111;
    9. };
    10. }

    这样就把数据读取出来了。

4、创建和写入plist文件

在开发过程中,有时候需要把程序的一些配置保存下来,或者游戏数据等等。 这时候需要写入Plist数据。

写入的plist文件会生成在对应程序的沙盒目录里。

接着上面读取plist数据的代码,加入了写入数据的代码,

  1. <strong>- (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. //读取plist
  5. NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"plistdemo" ofType:@"plist"];
  6. NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
  7. NSLog(@"%@", data);
  8. //添加一项内容
  9. [data setObject:@"add some content" forKey:@"c_key"];
  10. //获取应用程序沙盒的Documents目录
  11. NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
  12. NSString *plistPath1 = [paths objectAtIndex:0];
  13. //得到完整的文件名
  14. NSString *filename=[plistPath1 stringByAppendingPathComponent:@"test.plist"];
  15. //输入写入
  16. [data writeToFile:filename atomically:YES];
  17. //那怎么证明我的数据写入了呢?读出来看看
  18. NSMutableDictionary *data1 = [[NSMutableDictionary alloc] initWithContentsOfFile:filename];
  19. NSLog(@"%@", data1);
  20. // Do any additional setup after loading the view, typically from a nib.
  21. }
  22. </strong>
  23. 在获取到自己手工创建的plistdemo.plist数据后,在这些数据后面加了一项内容,证明输入写入了。

    怎么证明添加的内容写入了呢?下面是打印结果:

    NSCoding协议

    由于Sqlite数据库的blob数据和NSData的兼容比较好,我想尝试把一个NSArray存入Sqlite。Sqlite不支持数组的直接存储,所以我寻找了一些数组转化为NSData的方法。网上大多数的解决办法都是针对于字符数组,eg:

    NSArray*array01=
    [[NSArrayalloc]initWithObjects:@"1",@"2",@"3",nil];

    这种数组的解决办法很简单,循环遍历数组,然后把每次取出的NSString转码成NSData类型,然后对最终的NSData数据进行一次次的循环拼接,实现把数组中所有的字符串拼接成一个完整的NSData,eg:

    NSMutableData *data01
    =[[NSMutableDataalloc]init];

    for (NSString *str in array01) {

    NSString*newStr = [str stringByAppendingString:@","];//添加间隔,为了区分数组的每个元素

    NSData *temp =[newStr dataUsingEncoding:NSASCIIStringEncoding];//创建中间变量存储将nsstring转码成nsdata的数据

    [data01 appendData:temp];//data数据拼接到最终数据data01上

    [temp release];

    }

    这样就实现了一个NSArray字符数组到NSData的转换。转换回来也很简单,把NSData转换成NSString,然后将NSString分割(这也就是我们为str添加末尾“,”的原因),eg:

    NSString *string01 = [[NSString alloc]initWithData:data01 encoding:NSUTF8StringEncoding];//将NSData数据转换成NSString类型数据

    NSArray *array02 = [string01 componentsSeparatedByString:@","];//字符串根据@“,”拆分成一个数组,将数组还原

    但是这样太麻烦了,经过了这么多次转换,我就在思考有没有更简单的方法,NSData和NSArray的直接转换。无意间看到了一个网站,我看到了一个方法,运用的是“归档”,下面我们来说一下这个方法的实现,eg:

    NSData *data02 = [NSKeyedArchiver archivedDataWithRootObject:array01];//通过归档对NSArray进行转码,保存了数组的序列

    一句代码就完成了!而且还保存了数组的有序性。转换回去也同样是一句话哦:),eg:

    NSArray *array02 = [NSKeyedUnarchiver unarchiveObjectWithData:data02];//通过发归档进行NSData反转码,返回有序列的数组

    是不是很方便呢??不是很,是太方便了吧。但是这个时候问题又来了,我如果在数组里存放的不是NSString类型的对象呢,如果存了自定义的类,是否也可以自动跳转呢?(有点异想天开。。),于是我自己创建了一个Student类,尝试相同的方式转换,发现程序无法运行了,问了同事,他告诉了我这个秘密。原来所有原生的类都是实现了NSCoding协议,在归档的过程中进行了转码,所以才可以归档成功。我研究了一下NSCoding协议,下面是API给我们的信息:

    NSCoding Protocol Reference

    encodeWithCoder:

    Encodes the receiver using a given archiver. (required)

    - (void)encodeWithCoder:(NSCoder *)encoder

    Parameters
    encoder

    An archiver object.

    Availability
    • Available in iOS 2.0 and later.
    Declared In

    NSObject.h

    initWithCoder:

    Returns an object initialized from data in a given unarchiver.(required)

    - (id)initWithCoder:(NSCoder *)decoder

    Parameters
    decoder

    An unarchiver object.

    Return Value

    self, initialized using the datain decoder.

    Availability
    • Available in iOS 2.0 and later.
    Declared In

    NSObject.h

    NSCoding协议中只有两个方法,都是require的方法,一个是把本身的类进行转码,一个是逆转换成类对象,返回一个对象,我们实战一下这个协议的用法,看看是否好用,首先写一个自定义Student类:

    @interfaceStudent : NSObject<NSCoding>

    @property (nonatomic, retain) NSString *name;

    @property (nonatomic, retain) NSString *ID;

    -(Student *)initWithName :(NSString*)newName

    and : (NSString *)newID;

    @end

    Student类需要实现协议NSCoding,.m文件中是这样的:

    @implementationStudent

    @synthesize name = _name,ID = _ID;

    //初始化学生类

    -(Student *)initWithName:(NSString *)newName and:(NSString *)newID{

    self = [super init];

    if (self) {

    self.name = newName;

    self.ID= newID;

    }

    return self;

    }

    //学生类内部的两个属性变量分别转码

    -(void)encodeWithCoder:(NSCoder *)aCoder{

    [aCoder encodeObject:self.name forKey:@"name"];

    [aCoder encodeObject:self.IDforKey:@"ID"];

    }

    //分别把两个属性变量根据关键字进行逆转码,最后返回一个Student类的对象

    -(id)initWithCoder:(NSCoder *)aDecoder{

    if (self = [super init]) {

    self.name = [aDecoder decodeObjectForKey:@"name"];

    self.ID= [aDecoder decodeObjectForKey:@"ID"];

    }

    return self;

    }

    @end

    自定义类Student实现了NSCoding协议以后,就可以进行归档转换了,具体实现:

    Student *stu1 = [[Student alloc]initWithName:@"124" and:@"111"];//学生对象stu1

    Student *stu2 = [[Student alloc]initWithName:@"223" and:@"222"];//学生对象stu2

    NSArray *stuArray =[NSArray arrayWithObjects:stu1,stu2, nil];//学生对象数组,里面包含stu1和stu2

    NSData *stuData = [NSKeyedArchiver archivedDataWithRootObject:stuArray];//归档

    NSLog(@"data = %@",stuData);

    NSArray *stuArray2 =[NSKeyedUnarchiver unarchiveObjectWithData:stuData];//逆归档

    NSLog(@"array2 = %@",stuArray2);

    运行结果如下:

    2012-09-25 10:44:24.944MagazineDemo[720:f803] data = <62706c69 73743030d4010203 0405082b 2c542474 6f705824 6f626a65 63747358 2476657273696f6e 59246172 63686976 6572d106 0754726f 6f748001 aa090a1117181920 24252655 246e756c 6cd20b0c 0d105a4e 532e6f62
    6a65637473562463 6c617373 a20e0f80 02800680 09d31213 0c141516 524944546e616d65 80048003 80055331 32345331 3131d21a 1b1c1f58 24636c6173736573 5a24636c 6173736e 616d65a2 1d1e5753 74756465 6e74584e534f626a 65637457 53747564 656e74d3 12130c21 22168008 8007800553323233
    53323232 d21a1b27 2aa22829 574e5341 72726179 584e534f626a6563 74574e53 41727261 79120001 86a05f10 0f4e534b 6579656441726368 69766572 00080011 0016001f 00280032 0035003a 003c0047004d0052 005d0064 00670069 006b006d 00740077 007c007e 008000820086008a 008f0098
    00a300a6 00ae00b7 00bf00c6 00c800ca 00cc00d000d400d9 00dc00e4 00ed00f5 00fa0000 00000000 02010000 00000000002d0000 00000000 00000000 00000000 010c>

    2012-09-25 10:44:24.944MagazineDemo[720:f803] array2 = (

       "<Student: 0x6883490>",

       "<Student: 0x68dd3c0>"

    )

    成功啦:)自定义类Student转码成功.

时间: 2024-10-18 07:16:58

ios的文件加载和保存的相关文章

OC基础回顾(十四)文件加载与保存

Cocoa提供两个处理文件的通用类:属性列表和对象编码. 1.属性列表 在Cocoa中,有一个类名为属性列表(property list)的对象,通常简写为plist.这些列表包含 Cocoa知道如何操作的一组对象.具体来讲,Cocoa知道如何将它们保存到文件中并进行加载.属性列表类包括NSArray.NSDictionary.NSString.NSNumber.NSDate和NSData,以及它们的可修改形态变体(只要它们拥有前缀为Mutable的类). 1.1 NSDate NSDate是C

Cocos2d之Texture2D类详解之将文件加载成Texture2D对象

一.声明 笔者以cocos2d框架cocos2d-x-3.3rc0版本的源码做分析.本文为笔者原创,允许转载和分享,只要注明文章出处即可. 二.简介 Texture2D类简介 Texture2D类允许开发者用图像.文本信息和简单的数据来创建OpenGL2D纹理.被创建的纹理拥有两个维度.根据开发者创建Texture2D对象方式的不同,实际图像的尺寸可能比生成的纹理的尺寸要小,而且纹理的内容是倒置的. 像素格式 在计算机图形学中,人们用每个像素在内存中的总位数以及分别存储红.蓝.绿和alpha(阿

【iOS】网络加载图片缓存与SDWebImage

加载网络图片可以说是网络应用中必备的.如果单纯的去下载图片,而不去做多线程.缓存等技术去优化,加载图片时的效果与用户体验就会很差. 一.自己实现加载图片的方法 tips: *iOS中所有网络访问都是异步的.(自己开线程去下载) *普通为模型增加UIImage属性的方法做的是内存缓存(下次启动还需要从网络重新加载), 而要做本地缓存的话,还要自己手动存储网络上下载的图片. *为了加快访问, 还需要自己去弄缓存.(内存缓存或者本地缓存) *当图片没有下载完成时,还要设置占位图片. 以下代码用NSOp

JS文件加载:比较async和DOM Script

async与script动态加载都能使文件异步加载,本文叙述它们对页面渲染和load加载的影响方面. 目前我用demo.js作为执行文件操作.代码: var now = function() { return +(new Date()); } var t_s = now(); while(now() - t_s < 2000) { } 用sleep.php作为请求文件操作.代码: <?php sleep(3); echo 'var bb'; ?> 1. 一般script标签加载 <

ELF文件加载与动态链接(一)

ELF格式文件简单介绍 关于ELF文件的详细介绍,推荐阅读: ELF文件格式分析 —— 滕启明.ELF文件由ELF头部.程序头部表.节区头部表以及节区4部分组成. 通过objdump工具和readelf工具,可以观察ELF文件详细信息. ELF文件加载过程分析 从编译.链接和运行的角度,应用程序和库程序的链接有两种方式.一种是静态链接,库程序的二进制代码链接进应用程序的映像中:一种是动态链接,库函数的代码不放入应用程序映像,而是在启动时,将库程序的映像加载到应用程序进程空间. 在动态链接中,GN

ios UITableView 异步加载图片并防止错位

UITableView 重用 UITableViewCell 并异步加载图片时会出现图片错乱的情况 对错位原因不明白的同学请参考我的另外一篇随笔:http://www.cnblogs.com/lesliefang/p/3619223.html . 当然大多数情况下可以用 SDWebImage, 这个库功能强大,封装的很好.但自己重头来写可能对问题理解的更深. SDWebImage 有点复杂,很多人也会参考一下封装出一套适合自己的类库. 基本思路如下: 1 扩展(category) UIImage

unity UTF8格式加载和保存xml

UTF8格式加载xml string xmlPath="D:/xxx.xml" FileLoader fileLoader=new FileLoader(); fileLoader.loadAsync(xmlPath); fileLoader.onComplete-=onloadXmlComplete; private void onloadXmlComplete(byte[][] bytesList){ fileLoader.onComplete-=onloadXmlComplete

Java基础之Throwable,文件加载

Java中的异常与错误都继承自Throwable,Exception又分为运行时异常(RuntimeException)和编译时异常. 运行时异常是程序的逻辑不够严谨或者特定条件下程序出现了错误,例如做除法运算时除数为0,运行时异常Java是不要求一定去try,catch进行捕获的.我们调试代码要减少的就是运行时异常,随着代码的调试运行时异常被捕获,程序的健壮性也就得到了提升. 编译时异常例如文件未找到异常,IO异常,SQLException等,这些是可以预知的异常(checked Except

Java 文件变化加载监控,实时监控文件加载之Commons-io

Java 文件监控,实时监控文件加载 ,Commons-io中已有此功能的实现,如下代码所示: package wy.util.filemonitor; import java.io.File; import org.apache.commons.io.monitor.FileAlterationListenerAdaptor; import org.apache.commons.io.monitor.FileAlterationObserver; import org.apache.log4j