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

持久化数据有很多方法,但是常用的有以下几种,各有各的优势!
这几种方法几乎都是以文件的形式保存在程序当前目录下。
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_v2sqlite3_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语句的长度。如果小于0sqlite会自动计算它的长度(把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个参数:?号的索引。前面preparesql语句里有一个?号,假如有多个?号怎么插入?方法就是改变 bind_blob 函数第2个参数。参数为1,表示这里插入的值要替换 stat的第一个?号(这里的索引从1开始计数,而非从0开始)。如果你有多个?号,就写多个 bind_blob语句,并改变它们的第2个参数就替换到不同的?号。如果有?号没有替换,sqlite为它取值null

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

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

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

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

拟机执行字节码,执行过程是一个步进(stepwise)的过程,每一步(step)sqlite3_step()启动,并由VDBEsqlite拟机)执行一段字节码。由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];

依赖关系



主要由以下几个类组合使用
//获取管理对象       后缀名为xcdatamodeldCore Data文件做以下调用

NSURL *modelURL = [[NSBundlemainBundleURLForResource:@"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!");
    }



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值