您的位置:首页 > 移动开发 > IOS开发

IOS之持久化数据的常用5种方法

2013-09-30 22:50 429 查看
持久化数据有很多方法,但是常用的有以下几种,各有各的优势!

这几种方法几乎都是以文件的形式保存在程序当前目录下。

NSHomeDirectory()

NSSearchPathForDirectoriesInDomains

这两个函数是用来获取当前APP引用的目录的

1.NSUserDefaults 以键值对的形式保存在系统配置里。 (数据量少的情况下使用,比如说设置登录信息之类的)

例如:

NSString *string = [NSString stringWithString @"hahaha"];

NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];

[ud setObject:string forKey:@"myKey"];

NSString *value;

value = [ud objectForKey:"myKey"];

2.plist属性列表 (适合简单数据类型,比如说一些配置信息之类的)

这种可以使用NSArrary, NSDictionary等这些工具类直接写入文件

使用 arrayWithContentsOfFile:(NSString *)aPath
来从文件获取数据

使用 writeToFile:atomically: 来把数据写入文件

3.归档 (经过加密的保存数据方式,也是以键值对形式保存读取的.)

例:
所有归档对象必须支持NSCoding协议
和 NSCopying(支持对象复制)

//写入归档文件

NSArray *list = @[[NSArray allc] initWithObjects:@"one", @"two", nil];

NSMutableData *data = [[NSMutableData alloc] init];

NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

[archiver encodeObject:list forKey:@"keyValue"];

[archiver finishEncoding];

BOOL success = [data writeToFile:@"/path/to/archive" atomically:YES];

//成功返回YES

//读取归档文件

NSArray *list = nil;

NSData *data = [[NSData alloc] initWithContentsOfFile:@"/path/to/archive"];

NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

list = [unarchiver decodeObjectForKey:@"keyValue"];

[unarchiver finishDecoding];

//至此已经完成

4.SQLite3 这是一个小型的数据库 (支持复杂的数据查询和存储)

使用前得引入动态库 libsqlite3.dylib, 使用过程中的字符串类型必须是标准C字符串.

sqlite3_open() //用来打开数据库

sqlite3_prepare_v2() //操作数据库前的准备

sqlite3_step() //分步查询或者执行

sqlite3_column() //查询过后获取列值

sqlite3_bind_*() //当你想要插入或者更新数据时来绑定的内容

sqlite3——reset() //重置这个语句,回到重新绑定的状态

sqlite3_finalize() //销毁这个对象

sqlite3_close() //关闭数据库

sqlite3_exec是sqlite3_prepare_v2,sqlite3_step()和sqlite3_finalize()的封装,能让程序多次执行sql语句而不要写许多重复的代码。

Sqlite3_exec接口执行0或多个UTF-8编码的,分号分割的sql语句,传到第二个参数中。如果sqlite3_exec的第三个参数回调函数指针不为空,那么它会为每个来自执行的SQL语句的结果行调用(也就是说回调函数会调用多次,上面例子中会返回2个结果行,因而会被执行2次),第4个参数是传给回调函数的第一个参数,如果回调函数指针为空,那么回调不会发生同时结果行被忽略。

如果在执行sql语句中有错误发生,那么当前的语句的执行被停止,后续的语句也被跳过。第五个参数不为空的时候,它被分配内存并写入了错误信息,所以在sqlite3_exec后面需要调用sqlite3_free去释放这个对象以防止内存泄露

回调函数:

int (*callback)(void*,int,char**,char**), /* Callback function */

第一个参数通过sqlite3_exec的第第四个参数传入的

第二个参数是结果行的列数

第三个参数是行中列数据的指针

第四个参数是行中列名称的指针

查询数据

int sqlite3_get_table(sqlite3*, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg );

第1个参数是前面open函数得到的指针。

第2个参数const char *sql是一条 sql语句,以\0结尾。

第3个参数是查询结果,它是一维数组,它内存布局是:第一行是字段名称,后面是紧接着是每个字段的值。

第4个参数是查询出多少条记录(即查出多少行)。

第5个参数是多少个字段(多少列)。

第6个参数是错误信息。

操作二进制

sqlite3_stmt * 所表示的内容看成是 sql语句,但是实际上它不是我们所熟知的sql语句。它是一个已经把sql语句解析了的、用sqlite自己标记记录的内部数据结构。要插入二进制,前提是这个表的字段的类型是
blob 类型。

假设有一张表:

create table Tbl_2( ID integer, file_content blob )

首先声明

sqlite3_stmt * stat;

然后,把一个 sql 语句解析到 stat结构里去:

// sqlite3_prepare 接口把一条SQL语句编译成字节码留给后面的执行函数.使用该接口访问数据库是当前比较好的的一种方法.

sqlite3_prepare( db, “insert into Tbl_2( ID, file_content) values( 10, ? )”, -1, &stat, 0 );

上面的函数完成 sql 语句的解析

第一个参数跟前面一样,是个 sqlite3 * 类型变量,

第二个参数是一个 sql 语句。这个 sql语句特别之处在于 values里面有个 ?号。在sqlite3_prepare函数里,?号表示一个未定的值,它的值等下才插入。

第三个参数我写的是-1,这个参数含义是前面 sql语句的长度。如果小于0,sqlite会自动计算它的长度(把sql语句当成以\0结尾的字符串)。

第四个参数是 sqlite3_stmt 的指针的指针。解析以后的sql语句就放在这个结构里。

第五个参数为0就可以了。

如果这个函数执行成功(返回值是 SQLITE_OK 且 stat不为NULL),那么下面就可以开始插入二进制数据。

sqlite3_bind_blob( stat, 1, pdata, (int)(length_of_data_in_bytes), NULL );

// pdata为数据缓冲区,length_of_data_in_bytes为数据大小,以字节为单位

这个函数一共有5个参数。

第1个参数:是前面prepare得到的 sqlite3_stmt *类型变量。

第 2个参数:?号的索引。前面prepare的sql语句里有一个?号,假如有多个?号怎么插入?方法就是改变
bind_blob 函数第2个参数。参数为1,表示这里插入的值要替换 stat的第一个?号(这里的索引从1开始计数,而非从0开始)。如果你有多个?号,就写多个
bind_blob语句,并改变它们的第2个参数就替换到不同的?号。如果有?号没有替换,sqlite为它取值null。

第3个参数:二进制数据起始指针。

第4个参数:二进制数据的长度,以字节为单位。

第5个参数:是个析构回调函数,告诉sqlite当把数据处理完后调用此函数来析够你的数据。但是一般都填NULL,需要释放的内存自己用代码来释放。

bind完了之后,二进制数据就进入了你的“sql语句”里了。你现在可以把它保存到数据库里:

虚 拟机执行字节码,执行过程是一个步进(stepwise)的过程,每一步(step)由sqlite3_step()启动,并由VDBE(sqlite虚拟机)执行一段字节码。由sqlite3_prepare编译字节代码,并由sqlite3_step()启动虚拟机执行。在遍历结果集的过程中,它返回
SQLITE_ROW,当到达结果末尾时,返回SQLITE_DONE

int result = sqlite3_step( stat );

通过这个语句,stat 表示的sql语句就被写到了数据库里。

最后,要把 sqlite3_stmt
结构给释放:sqlite3_finalize( stat ); //把刚才分配的内容析构掉

读出二进制

先声明 sqlite3_stmt *
类型变量:

sqlite3_stmt * stat;

然后,把一个 sql 语句解析到 stat结构里去:

sqlite3_prepare( db, “select * from Tbl_2”, -1, &stat, 0 );

当 prepare
成功之后(返回值是 SQLITE_OK ),开始查询数据。

int result = sqlite3_step( stat );

这一句的返回值是 SQLITE_ROW
时表示成功(不是 SQLITE_OK )。

你可以循环执行 sqlite3_step 函数,一次 step查询出一条记录。直到返回值不为 SQLITE_ROW时表示查询结束。

然后开始获取第一个字段:ID 的值。ID是个整数,用下面这个语句获取它的值:

int id = sqlite3_column_int( stat, 0 ); //第2个参数表示获取第几个字段内容,从0开始计算。

下面开始获取 file_content 的值,因为 file_content是二进制,因此我需要得到它的指针,还有它的长度:

const void * pFileContent = sqlite3_column_blob( stat, 1 );

int len = sqlite3_column_bytes( stat, 1 );

这样就得到了二进制的值。

把 pFileContent
的内容保存出来之后,

不要忘了释放 sqlite3_stmt
结构:

sqlite3_finalize( stat ); //把刚才分配的内容析构掉

重复使用 sqlite3_stmt
结构

如果你需要重复使用 sqlite3_prepare
解析好的 sqlite3_stmt 结构,需要用函数: sqlite3_reset。

result = sqlite3_reset(stat);

这样, stat
结构又成为 sqlite3_prepare 完成时的状态,你可以重新为它 bind内容。

事务处理

sqlite 是支持事务处理的。如果你知道你要同步删除很多数据,不仿把它们做成一个统一的事务。通常一次 sqlite3_exec就是一次事务,如果你要删除1万条数据,sqlite就做了1万次:开始新事务->删除一条数据->提交事务->开始新事务
->… 的过程。这个操作是很慢的。因为时间都花在了开始事务、提交事务上。你可以把这些同类操作做成一个事务,这样如果操作错误,还能够回滚事务。

事务的操作没有特别的接口函数,它就是一个普通的 sql 语句而已:

分别如下:

int result;

result = sqlite3_exec( db, "begin transaction", 0, 0, &zErrorMsg ); //开始一个事务

result = sqlite3_exec( db, "commit transaction", 0, 0, &zErrorMsg ); //提交事务

result = sqlite3_exec( db, "rollback transaction", 0, 0, &zErrorMsg ); //回滚事务

5. Core Data苹果自带的数据管理工具
(
底层是SQLite3)

数据持久化存储

数据最终的存储类型可以是:SQLite数据库,XML,二进制,内存里,或自定义数据类型

名词解释

(1)NSManagedObjectContext(被管理的数据上下文)

操作实际内容(操作持久层)

作用:插入数据,查询数据,删除数据

(2)NSManagedObjectModel(被管理的数据模型)

数据库所有表格或数据结构,包含各实体的定义信息

作用:添加实体的属性,建立属性之间的关系

操作方法:视图编辑器,或代码

(3)NSPersistentStoreCoordinator(持久化存储助理)

相当于数据库的连接器

作用:设置数据存储的名字,位置,存储方式,和存储时机

(4)NSManagedObject(被管理的数据记录)

相当于数据库中的表格记录

(5)NSFetchRequest(获取数据的请求)

相当于查询语句

(6)NSEntityDescription(实体结构)

相当于表格结构

(7)后缀为.xcdatamodeld的包

里面是.xcdatamodel文件,用数据模型编辑器编辑

编译后为.momd或.mom文件

(8)NSAttributeDescription

实体的attribut

(9)NSRelationshipDescription

指代实体间的relationship

(10)NSFetchedPropertyDescription

fetched属性

(11)NSSortDescriptor (排序)

NSSortDescriptor *sortDescriptor = [[NSSortDescriptoralloc]initWithKey:@"字段名"ascending:NO];

依赖关系



主要由以下几个类组合使用

//获取管理对象 后缀名为xcdatamodeld的Core
Data文件做以下调用

NSURL *modelURL = [[NSBundlemainBundle] URLForResource:@"Core_Data_Peristence"withExtension:@"momd"];

NSManageObjectModel *model = [[NSManageObjectModel alloc] initWithContentsOfURL:modelURL];

NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManageObjectModel:];

//设置数据底层的存储类型和文件名

NSURL *url= [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] LastObject]];

NSURL *mUrl = [url URLByAppendingPathComponent:@"core_data_persitence.sqlite"];

[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:mUrl options:nil error:nil];

//创建对象管理上下文并关联数据

NSManagedObjectContext *context = [[NSManageObjectContext alloc] init];

[context setPersistentStoreCoordinator:coordinator];

NSError *error;

//创建一个抓取数据的对象管理

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

//创建一个实体对象描述 ,相当于选择一个数据库里的表Line

NSEntityDescription *entityDescription = [NSEntityDescription

entityForName:@"Line"

inManagedObjectContext:context];

//设置抓取数据的条件筛选

NSPredicate *pred = [NSPredicate predicateWithFormat:@"name = %s", @"xukai"];

//结合表和条件准备开始抓取

[request setEntity:entityDescription];

[request setPredicate:pred];

//用来保存抓取出来的结果

NSmanagedObject* object = nil;

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

if( [objects count] > 0 )

object = [objects objectAtIndex:0];

else

object = [NSEntityDescription

insertNewObjectForEntityForName:@"Line"

inManagedObjectContext:context];

[object setValue:29 forKey:@"age"];

[object setValue:1 forKey:@"sex"];

[context save:&error];

//以下是对查询以distinct 不重复查询的例子
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"People" inManagedObjectContext:managedObjectContext];
request.entity = entity;
request.propertiesToFetch = [NSArray arrayWithObject:[[entity propertiesByName] objectForKey:@"age"]];
request.returnsDistinctResults = YES;
request.resultType = NSDictionaryResultType;

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptors]];
[sortDescriptor release];

NSError *error = nil;
NSArray *distincResults = [managedObjectContext executeFetchRequest:request error:&error];
// Use the results
[request release];


CoreData数据库升级方法

1.选中你的mydata.xcdatamodeld文件,选择菜单editor->Add Model Version 比如取名:mydata2.xcdatamodel

2.设置当前版本 选择上级mydata.xcdatamodeld ,在inspector中的Versioned Core Data Model选择Current模版为mydata2

3.修改新数据模型mydata2,在新的文件上添加字段及表
4. 修改代码
NSPersistentStoreCoordinator 初始化方案:
你以前的代码可能是这样:
if (![persist addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrloptions:nil error:&err]) {
NSAssert(0, @"persist init failed!");
}
修改成:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

if (![persist addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrloptions:options error:&err]) {
NSAssert(0, @"persist init failed!");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: