数据持久化

一、数据持久化概述

数据持久化就是数据的永久存储。其本质是将数据保存为文件,存到程序的沙盒中。

1、数据持久化的方式

1.1 writeToFile:简单对象写入文件

1.2 NSUserDefaults:应用程序偏好设置

1.3 Sqlite:轻量级关系型数据库,不能直接存储对象(NSData除外),需要用到一些SQL语句,先将复杂对象归档(对象->NSData)

1.4 CoreData:对象型数据库,实质是将数据库的内部存储细节封装

1.5 Plist文件

2、应用程序沙盒

每一应用程序都有自己的应用沙盒,沙盒的本质就是一个文件夹,名字是随机分配的。

与其他应用程序沙盒隔离,应用程序本身只能访问自己沙盒的数据。(iOS8+对沙盒之间的访问部分开放)

2.1应用程序包(.app)

包含了应用程序中所用到的所有资源文件和可执行文件(Base on Unix)。iOS8时,app不存储在沙盒中,有单独的文件夹存储所有程序的app包。

2.2 HomeDirectory

Documents:保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录

tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录

Library/Caches:保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据

Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录

2.3 获取沙盒路径

沙盒根目录:NSHomeDirectory();

沙盒临时目录:NSTemporaryDirectory();

Library/Preferences:NSUserDefaults

//1.获取沙盒中Documents文件夹的路径

//第一种方式:

NSString*documentPath =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES).lastObject;//NO, path = @"~/"(相对路径); YES 绝对路径

NSLog(@"%@", documentPath);

//第二种方式: (不建议采用,因为新版本的操作系统可能会修改目录名)

NSString*homePath =NSHomeDirectory();

NSString*documentPath2 = [homePathstringByAppendingPathComponent:@"library/caches"];

NSLog(@"%@", documentPath2);

//2.获取应用程序包路径(.app)

NSLog(@"%@", [NSBundlemainBundle].resourcePath);

二、简单对象持久化

1、简单对象

NSString\NSArray\NSDictionary\NSData

使用writeToFile:方法,将数据存储为.plist文件

atomically参数为是否写入缓存

//字符串

NSString*string =@"I?U";

//数组

NSArray*array =@[@"张三",@"李四",@"王五"];

//字典

NSDictionary*dictionary =@{@"name":@"张三",@"age":@"20",@"sex":@"男"};

//NSData

UIImage*image = [UIImageimageNamed:@"1.jpg"];

NSData*data =http://my.oschina.net/zooyf/blog/UIImageJPEGRepresentation(image,1);

//1.拼接存储路径

NSString*strPath = [documentPathstringByAppendingPathComponent:@"string.txt"];

NSString*arrayPath = [documentPathstringByAppendingPathComponent:@"array.txt"];

NSString*dicPath   = [documentPathstringByAppendingPathComponent:@"dict.txt"];

NSString*dataPath  = [documentPathstringByAppendingPathComponent:@"data.txt"];

//2.写入文件

[stringwriteToFile:strPathatomically:YESencoding:NSUTF8StringEncodingerror:nil];

[arraywriteToFile:arrayPathatomically:YES];

[dictionarywriteToFile:dicPathatomically:YES];

[datawriteToFile:dataPathatomically:YES];

//3.读取文件内容

NSString*fileString = [NSStringstringWithContentsOfFile:strPathencoding:NSUTF8StringEncodingerror:nil];

NSArray*fileArray = [NSArrayarrayWithContentsOfFile:arrayPath];

NSDictionary*fileDict = [NSDictionarydictionaryWithContentsOfFile:dicPath];

NSData*fileData = http://my.oschina.net/zooyf/blog/[NSDatadataWithContentsOfFile:dataPath];

NSLog(@"%@", fileString);

NSLog(@"%@", fileArray);

NSLog(@"%@", fileDict);

NSLog(@"%@", fileData);

2、文件管理类:NSFileManager

2.1、功能

NSFileManager使用defaultManager创建单例对象。可以创建文件夹,可以删除、移动、创建文件,判断文件是否存在。

2.2、使用

//缓存文件夹所在路径

NSString*cachesPath =NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES).lastObject;

NSLog(@"%@", cachesPath);

//在cachesPath路径下创建一个文件夹

NSString*directoryPath = [cachesPathstringByAppendingPathComponent:@"path"];

NSFileManager*fileManager = [NSFileManagerdefaultManager];//创建文件管理类单例对象

//根据路径创建文件夹

NSDictionary*fileDate =@{@"createTime":@"2015-9-9"};

[fileManagercreateDirectoryAtPath:directoryPathwithIntermediateDirectories:YESattributes:fileDateerror:nil];

//根据路径创建文件(只能写入NSData类型的数据)

[fileManagercreateFileAtPath:directoryPathcontents:dataattributes:fileDate];

//删除文件

[fileManagerremoveItemAtPath:dicPatherror:nil];//删除~/documents/dict.txt

3、NSUserDefaults

NSUserDefaults*defaults = [NSUserDefaultsstandardUserDefaults];//单例

[defaultssetValue:@"yfyfyfyfyfyfyfy"forKey:@"username"];

[defaultssetValue:@"123"forKey:@"password"];

//注意:UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法强制写入到文件中

[defaultssynchronize];

//读取

NSString*name = [defaultsvalueForKey:@"username"];

NSString*pwd = [defaultsvalueForKey:@"password"];

二、复杂对象持久化(NSKeyedArchiver)

1、复杂对象

复杂对象是在Foundation框架内不存在的数据类,无法通过writeToFile写入到文件内,且至少包含一个实例对象。

由于复杂对象无法通过writeToFile:方法写入文件,只能将复杂对象转化为NSData对象,再进行数据持久化。

2、NSCoding协议

@protocolNSCoding

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

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

@end

3、复杂对象写入文件

Person.h

//复杂对象归档一:遵守NSCoding协议

@interfacePerson :NSObject

@property(nonatomic,assign)NSIntegerage;

@property(nonatomic,retain)NSString*name;

@property(nonatomic,retain)NSString*gender;

@end

Person.m

#import"Person.h"

@implementationPerson//实现NSCoding协议

#pragma mark --进行编码--

- (void)encodeWithCoder:(NSCoder*)coder

{

//    [super encodeWithCode:coder];如果父类也遵守了NSCoding协议,确保继承的实例变量也能被编码,即也能被归档

[coderencodeObject:self.nameforKey:@"name"];

[coderencodeInteger:self.ageforKey:@"age"];

[coderencodeObject:self.genderforKey:@"gender"];

}

#pragma mark --进行解码--

- (id)initWithCoder:(NSCoder*)aDecoder

{

//    self = [super initWithCoder:aDecoder];确保继承的实例变量也能被解码,即也能被恢复

self= [superinit];

if(self) {

self.name= [aDecoderdecodeObjectForKey:@"name"];

self.gender= [aDecoderdecodeObjectForKey:@"gender"];

self.age= [aDecoderdecodeIntegerForKey:@"age"];

}

returnself;

}

@end

ViewController.m

类方法进行编码\解码(只能归档一个对象):

NSString*objPath = [cachesPathstringByAppendingPathComponent:@"person.txt"];

[NSKeyedArchiverarchiveRootObject:persontoFile:objPath];

Person*p2 = [NSKeyedUnarchiverunarchiveObjectWithFile:objPath];

实例方法(可以归档多个对象):

#pragma mark --对复杂对象进行持久化(归档\编码) --

//过程:(复杂对象->归档->NSData->writeToFile:)

Person*person = [[Personalloc]init];

person.name=@"yf";

person.age=20;

person.gender=@"man";

NSMutableData*mtData = http://my.oschina.net/zooyf/blog/[NSMutableDatadata];

//创建归档器

NSKeyedArchiver*archiver = [[NSKeyedArchiveralloc]initForWritingWithMutableData:mtData];

//进行归档

[archiverencodeObject:personforKey:@"person"];

//***结束归档

[archiverfinishEncoding];

//将归档之后的mtData写入文件

NSString*personPath = [cachesPathstringByAppendingPathComponent:@"person.txt"];

[mtDatawriteToFile:personPathatomically:YES];

NSLog(@"%@", personPath);

NSLog(@"%@", mtData);

#pragma mark --从文件中读取复杂对象(反归档\恢复\解码) --//过程:(读取文件(NSData)->反归档->复杂对象)

//读取

NSData*readData = http://my.oschina.net/zooyf/blog/[NSDatadataWithContentsOfFile:personPath];

//创建反归档工具

NSKeyedUnarchiver*unArchiver = [[NSKeyedUnarchiveralloc]initForReadingWithData:readData];

//使用反归档工具对readData进行反归档

Person*readPerson = [unArchiverdecodeObjectForKey:@"person"];

4、使用NSKeyedArchive进行深复制

比如对一个Person对象进行深复制

// 临时存储person1的数据

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1];

// 解析data,生成一个新的Person对象

Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];

// 分别打印内存地址

NSLog(@"person1:0x%x", person1); // person1:0x7177a60

NSLog(@"person2:0x%x", person2); // person2:0x7177cf0





OS中的数据持久化方式,基本上有以下四种:属性列表、对象归档、SQLite3和Core Data

NSUserDefaults,用于存储配置信息

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

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

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

1.属性列表

涉及到的主要类:NSUserDefaults,一般 [NSUserDefaults standardUserDefaults]就够用了

@interface User : NSObject

@property (nonatomic, assign) NSInteger userID;

@property (nonatomic, copy) NSString *name;

@end

使用方法

1).分开存取

// 存

[[NSUserDefaults standardUserDefaults] setInteger:userID forKey:@”userID”];

[[NSUserDefaults standardUserDefaults] setObject:name forKey:@”name”];

// 取

NSInteger uId = [[[NSUserDefaults standardUserDefaults] integerValueForKey:@”userID”];

NSString* name = [[NSUserDefaults standardUserDefaults] stringForKey:@”name”];

2).按对象存取

// 存

[[NSUserDefaults standardUserDefaults] setObject:self forKey:@”user”];

// 取

User* u = [[NSUserDefaults standardUserDefaults]objectForKey”@”user”];

2.对象归档

要使用对象归档,对象必须实现NSCoding协议.大部分Object C对象都符合NSCoding协议,也可以在自定义对象中实现NSCoding协议,要实现NSCoding协议,实现两个方法:

- (void) encodeWithCoder:(NSCoder *)encoder 与 -(void)initWithCoder:(NSCoder *)encoder

同时,建议对象也同时实现NSCopying协议,该协议允许复制对象,要实现NSCopying协议须实现 -(id)copyWithZone:(NSZone *)zone 方法 。

@interface User : NSObject

@property (nonatomic, assign) NSInteger userID;

@property (nonatomic, copy) NSString *name;

@end

@implementation User

// 以下两个方法一定要实现,不然在调用的时候会crash

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

{

// 这里放置需要持久化的属性

[aCoder encodeObject:[NSNumber numberWithInteger:self.userID] forKey:@”userID”];

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

}

- (id)initWithCoder:(NSCoder *)aDecoder

{

if (self = [self init])

{

//  这里务必和encodeWithCoder方法里面的内容一致,不然会读不到数据

self.userID = [[aDecoder decodeObjectForKey:@"userID"] integerValue];

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

}

return self;

}

// 使用方法

+ (BOOL)save {

NSError *error = nil;

// 确定存储路径,一般是Document目录下的文件

NSString* fileName = [self getFileName];

NSString* filePath = [self getFilePath];

if (![[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:&error]) {

NSLog(@”创建用户文件目录失败”);

return NO;

}

return [NSKeyedArchiver archiveRootObject:self toFile:[fileName:userId]];

}

@end

3.SQLite3

SQLite是一个开源的嵌入式关系数据库,它在2000年由D. Richard Hipp发布,它的减少应用程序管理数据的开销,SQLite可移植性好,很容易使用,很小,高效而且可靠。

SQLite嵌入到使用它的应用程序中,它们共用相同的进程空间,而不是单独的一个进程。从外部看,它并不像一个RDBMS,但在进程内部,它却是完整的,自包含的数据库引擎。 嵌入式数据库的一大好处就是在你的程序内部不需要网络配置,也不需要管理。因为客户端和服务器在同一进程空间运行。SQLite 的数据库权限只依赖于文件系统,没有用户帐户的概念。SQLite 有数据库级锁定,没有网络服务器。它需要的内存,其它开销很小,适合用于嵌入式设备。你需要做的仅仅是把它正确的编译到你的程序。

关于SQLite的开发资料较多,这里不再细说。只是建议不直接操作SQLite库,而是采用一些开源的第三方库来进行操作。比如:

FMDB:https://github.com/ccgus/fmdb.git

对SQLite都做了不错的封装。

4.Core Data

Core Data本质上是使用SQLite保存数据,但是它不需要编写任何SQL语句。

要使用Core Data,需要在Xcode中的数据模型编辑器中设计好各个实体以及定义好他们的属性和关系。之后,通过操作这些对象,结合Core Data完成数据的持久化:

NSManagedObjectContext *context = [appDelegate managedObjectContext];

NSError *error;

NSString *fieldName = [NSString stringWithFormat:@"test%d", i];

UITextField *theField = [self valueForKey:fieldName];

NSFetchRequest *request = [[NSFetchRequest alloc] init];

//创 建描述语句,需求Line对象。类似于在数据库中限定为Line表。

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"  inManagedObjectContext:context];

[request setEntity:entityDescription];

//创建限制性语句,类似于SQL语句中的 where lineNum = i

NSPredicate *pred = [NSPredicate predicateWithFormat:@"(lineNum = %d)", i];

[request setPredicate:pred];

NSManagedObject *theLine = nil;

NSArray *objects = [context executeFetchRequest:request error:&error];

if (objects == nil){

NSLog(@”There was an error!”);

// Do whatever error handling is appropriate

}

if ([objects count] > 0){    //如果符合条件的object存在,则取出

theLine = [objects objectAtIndex:0];

}

else {  //如果不存在,则插入一个新的.

theLine = [NSEntityDescription insertNewObjectForEntityForName:@"Line"

inManagedObjectContext:context];

[theLine setValue:[NSNumber numberWithInt:i] forKey:@”lineNum”];  //设置这个object的属性,coredata会自动将其写入sqlite

[theLine setValue:theField.text forKey:@"lineText"];

[request release];

}

下面是其取数据的过程:

Core_Data_PersistenceAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

NSManagedObjectContext *context = [appDelegate managedObjectContext];

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Line"

inManagedObjectContext:context];

NSFetchRequest *request = [[NSFetchRequest alloc] init];

[request setEntity:entityDescription];

NSError *error;

NSArray *objects = [context executeFetchRequest:request error:&error];

if (objects == nil)

{

NSLog(@”There was an error!”);

// Do whatever error handling is appropriate

}

//每一个对象在CoreData中都表示为一个NSManagedObject对象(类似于数据库表中的每一行),他的属性通过键/值 方式获取

for (NSManagedObject *oneObject in objects)

{

NSNumber *lineNum = [oneObject valueForKey:@"lineNum"];

NSString *lineText = [oneObject valueForKey:@"lineText"];

}

[request release];

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容