ios序列化与反序列化,本地化

你是用什么方法来持久保存数据的?这是在几乎每一次关于iOS技术的交流或讨论都会被提到的问题,而且大家对这个问题的热情持续高涨。本文主要从概念上把“数据存储”这个问题进行剖析,并且结合各自特点和适用场景给大家提供一个选择的思路,并不详细介绍某一种方式的技术细节。

谈到数据储存,首先要明确区分两个概念,数据结构和储存方式。所谓数据结构就是数据存在的形式。除了基本的NSDictionary、NSArray和NSSet这些对象,还有更复杂的如:关系模型、对象图和属性列表多种结构。而存储方式则简单的分为两种:内存与闪存。内存存储是临时的,运行时有效的,但效率高,而闪存则是一种持久化存储,但产生I/O消耗,效率相对低。把内存数据转移到闪存中进行持久化的操作称成为归档。

二者结合起来才是完整的数据存储方案,我们最常谈起的那些:SQLite、CoreData、NSUserDefaults等都是数据存储方案。当然在这些框架提供的方案之外,我们自己也可以按照个性化需求订制方案。这些存储方案侧重不同,支持的形式和方式也各不相同,在不同的使用场景下表现也是各有优劣。但万变不离其宗,无论什么方案都可以用下图来解释。

ios序列化与反序列化,本地化

图1,存储方案示意图

以下将对四种存储方式进行详细的介绍:

  • NSUserDefaults,用于存储配置信息

  • SQLite,用于存储查询需求较多的数据

  • CoreData,用于规划应用中的对象

  • 使用基本对象类型定制的个性化缓存方案

NSUserDefaults存储配置信息

NSUserDefaults被设计用来存储设备和应用的配置信息,它通过一个工厂方法返回默认的、也是最常用到的实例对象。这个对象中储存了系统中用户的配置信息,开发者可以通过这个实例对象对这些已有的信息进行修改,也可以按照自己的需求创建新的配置项。

ios序列化与反序列化,本地化

图2,笔者手机中[NSUserDefaults standardUserDefaults]内容

NSUserDefaults把配置信息以字典的形式组织起来,支持字典的项包括:字符串或者是数组,除此之外还支持数字等基本格式。一句话概括就是:基础类型的小数据的字典。操作方法几乎与NSDictionary的操作方法无异,另外还可以通过指定返回类型的方法获取到指定类型的返回值。

ios序列化与反序列化,本地化

图3,NSUserDefaults提供的指定返回类型的方法列表

NSUserDefaults的所有数据都放在内存里,因此操作速度很快,并还提供一个归档方法:+ (void)synchronize。开发者自定义的配置项(如图2中的最后一项 key:alkdjfkladsjfmm)会以plist格式的文件归档在相应应用目录的/Library/Preferences/[App_Bundle_Identifier].plist文件。再次初始化获得实例对象后,框架会把用户自定义的这个配置和系统配置合并得到完整数据。

SQLite存储查询需求较多的数据

iOS的SDK里预置了SQLite的库,开发者可以自建SQLite数据库。SQLite每次写入数据都会产生IO消耗,把数据归档到相应的文件。

SQLite擅长处理的数据类型其实与NSUserDefaults差不多,也是基础类型的小数据,只是从组织形式上不同。开发者可以以关系型数据库的方式组织数据,使用SQL DML来管理数据。 一般来说应用中的格式化的文本类数据可以存放在数据库中,尤其是类似聊天记录、Timeline等这些具有条件查询和排序需求的数据。

每一个数据库的句柄都会在内存中都会被分配一段缓存,用于提高查询效率。另一个方面,由于查询缓存,当产生大量句柄或数据量较大时,会出现缓存过大,造成内存浪费。

SQLite的使用起来要比NSUserDefaults复杂的多,因此建议开发者使用SQLite要搭配一个操作控件使用,可以简化操作。笔者开发的SQLight是一款对SQLite操作的封装,把相对复杂的SQLite命令封装成对象和方法,可以供大家参考。大家可以在Github上获取这个工程的代码进一步了解。

CoreData规划应用中对象

官方给出的定义是,一个支持持久化的,对象图和生命周期的自动化管理方案。严格意义上说CoreData是一个管理方案,他的持久化可以通过SQLite、XML或二进制文件储存。如官方定义所说,CoreData的作用远远不止储存数据这么简单,它可以把整个应用中的对象建模并进行自动化的管理。

ios序列化与反序列化,本地化

图4,官方文档中解释CoreData给出的对象图示例

正如上图所示,MyDocument是一个对象实例,有两个Collection:Employee和Department,存放各自的对象列表。MyDocument、Employee和Department三个对象以及他们之间的关系都通过CoreData建模,并可以通过save方法进行持久化。

从归档文件还原模型时CoreData并不是一次性把整个模型中的所有数据都载入内存,而是根据运行时状态,把被调用到的对象实例载入内存。框架会自动控制这个过程,从而达到控制内存消耗,避免浪费。

无论从设计原理还是使用方法上看,CoreData都比较复杂。因此,如果仅仅是考虑缓存数据这个需求,CoreData绝对不是一个优选方案。CoreData的使用场景在于:整个应用使用CoreData规划,把应用内的数据通过CoreData建模,完全基于CoreData架构应用。

苹果官方给出的一个示例代码,结构相对简单,可以帮助大家入门CoreData。

obj-c中有一类对象:NSArray,NSDictionary,NSString,NSNumber,NSDate,NSData以及它们的可变版本(指NSMutableArray,NSMutableDictionary...这一类) ,都可以方便的将自身的数据以某种格式(比如xml格式)序列化后保存成本地文件。



示例代码:NSArrayTest.h

#import <Foundation/Foundation.h>
#define FILE_NAME @"/tmp/data.txt"
 
@interface NSArrayTest : NSObject {  
}
 
-(void) Test;
 
@end
#import "NSArrayTest.h"
 
@implementation  NSArrayTest
 
-(void) Test
{
             NSArray *arr = [NSArray arrayWithObjects:@"one",@"two",@"three",nil];      //注:最后一个要以nil结尾
             [arr writeToFile:FILE_NAME atomically:YES];         //(序列化为xml格式后)保存文件
     
             NSArray *arr2 = [NSArray arrayWithContentsOfFile:FILE_NAME];         //read file
             NSLog(@"%@",arr2);
 
}
 
@end


运行结果:NSArrayTest.m

2011-03-03 14:20:01.501 pList[1246:a0f] (
    one,
    two,
    three
)

如果查看/tmp/data.txt,能看到下面的内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
	<string>one</string>
	<string>two</string>
	<string>three</string>
</array>
</plist>

即NSArray默认是以xml格式来序列化对象的.

如果你用来存放数据的类是自己定义的,并不是上面这些预置的对象,那么就要借助正式协议NSCoding来实现序列化和反序列化。


比如,我们有一个自己的类Sample.h

#import <Foundation/Foundation.h>

@interface Sample : NSObject<NSCoding> {

	NSString* name;
	int magicNumber;
	float shoeSize;
	NSMutableArray *subThingies;	
}

@property(copy) NSString* name;
@property int magicNumber;
@property float shoeSize;
@property (retain) NSMutableArray *subThingies;


-(id) initWithName:(NSString *)n magicNumber:(int)m shoeSize:(float) ss;

@end

这里我们定义几个不同类型的属性,有字符串,有整数,有浮点数,还有一个可变长的数组对象

Sample.m

#import "Sample.h"

@implementation Sample

@synthesize name;
@synthesize magicNumber;
@synthesize shoeSize;
@synthesize subThingies;

-(id) initWithName:(NSString *)n magicNumber:(int)m shoeSize:(float)ss
{
	if (self=[super init])
	{
		self.name = n;
		self.magicNumber = m;
		self.shoeSize = ss;
		self.subThingies = [NSMutableArray array];
	}
	return (self);
}

-(void) dealloc
{
	[name release];
	[subThingies release];
	[super dealloc];
}

//将对象编码(即:序列化)
-(void) encodeWithCoder:(NSCoder *)aCoder
{
	[aCoder encodeObject:name forKey:@"name"];
	[aCoder encodeInt:magicNumber forKey:@"magicNumber"];
	[aCoder	encodeFloat:shoeSize forKey:@"shoeSize"];
	[aCoder	encodeObject:subThingies forKey:@"subThingies"];
}

//将对象解码(反序列化)
-(id) initWithCoder:(NSCoder *)aDecoder
{
	if (self=[super init])
	{
		self.name = [aDecoder decodeObjectForKey:@"name"];
		self.magicNumber = [aDecoder decodeIntForKey:@"magicNumber"];
		self.shoeSize = [aDecoder decodeFloatForKey:@"shoeSize"];
		self.subThingies = [aDecoder decodeObjectForKey:@"subThingies"];
	}
	return (self);
	
}


-(NSString*) description
{
	NSString *description = [NSString stringWithFormat:@"%@:%d/%.1f %@",name,magicNumber,shoeSize,subThingies];
	return (description);
}


@end

注意其中的:encodeWithCoderinitWithCoder,这是NSCoding协议中定义的二个方法,用来实现对象的编码与解码。其实现也不复杂,利用的是key-value的经典哈希结构。当然一般在编码中,对于key的名字字符串,建议用define以常量方式事先定义好,以避免开发人员字符串键入错误。

测试一下:

#import <Foundation/Foundation.h>
#import "Sample.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];	

	Sample *s1 = [[Sample alloc] initWithName:@"thing1" magicNumber:42 shoeSize:10.5];	
	[s1.subThingies addObject:@"1"];
	[s1.subThingies addObject:@"2"];
	NSLog(@"%@",s1);	
	NSData	*data1 = [NSKeyedArchiver archivedDataWithRootObject:s1];//将s1序列化后,保存到NSData中
	[s1 release];
	[data1 writeToFile:@"/tmp/data.txt" atomically:YES];//持久化保存成物理文件
			
	NSData *data2 = [NSData dataWithContentsOfFile:@"/tmp/data.txt"];//读取文件	
	Sample *s2 = [NSKeyedUnarchiver unarchiveObjectWithData:data2];//反序列化	
    NSLog(@"%@",s2);
	 
	[pool drain];
    return 0;
}

运行结果:

2011-03-03 14:36:48.540 pList[1322:a0f] thing1:42/10.5 (
    1,
    2
)
2011-03-03 14:36:48.548 pList[1322:a0f] thing1:42/10.5 (
    1,
    2
)

查看/tmp/data.txt,能看到以下内容:

由于经过了编码,里面的内容没有象前面的NSArray那样可读性强。


有关序列化和反序列化内容转自如下地址:

http://www.cnblogs.com/yjmyzz/archive/2011/03/03/1969859.html


你可能感兴趣的:(ios序列化与反序列化,本地化)