在ios开发过程中,经常需要用到数据持久化工作。对于基本的配置信息等,NSUserDefault已经可以满足要求,但是对于大部分需要存储的信息,主要的方式有coredata建模或者sqlite写数据库的方法进行存储。现在针对coredata、sqlite以及常用的sqlite封装库进行研究和学习。
首先,针对四种数据持久化方式,进行基本的10w条数据插入得到性能耗时如下(由于sqlitepersistentobject未找到合适的批量插入方法,所以没有进行对比):
sqlite1470ms
fmdb 3088ms
coredata3418ms
从中可以看出,sqlite最快,基本都只用fmdb以及coredata等的一半时间。接下来我们逐个解析相关技术/库的操作以及使用。
【coreData】
coredata相信基本都不陌生,我们常用的主要就是Data Model了,有两种方法添加Data Model:新建工程时勾选或者添加Core Data->Data Model文件。采用第一种方法默认会有相关的代码生成,为了更好的了解Core Data是怎么load进来的,我们采用第二种方法进行演示。
首先,我们添加CoreData->Model Data模型,名字输入为coreData,即可在文件列表中见到coreData.xcdatamodeld文件。我们先添加如下Entities:
我们已经有了模型,那么该怎样使用呢。有一个NSManagedObjectModel的类,专门用来管理数据模型的。先从coreData.xcdatamodeld中初始化模型:
复制代码
- // initilize
- NSURL* modelURL = [[NSBundle mainBundle] URLForResource:@"coreData" withExtension:@"momd"];
- coreDataModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];</span>
- 我们可以理解为,coreDataModel就代表了该模型。
- 接下来,我们需要考虑,有了模型后,数据最终应该存在哪里?答案是:文件。接下来我们有另外一个类来管理模型跟文件之间的对应关系:
-
- [code]NSString* strInfoPath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"coreData.sqlite"];
- coreDataCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:coreDataModel];
- [coreDataCoordinator addPersistentStoreWithType:NSsqliteStoreType configuration:nil URL:[NSURL fileURLWithPath:strInfoPath] options:nil error:nil];</span>
- OK,现在文件<->模型 之间的映射关系也有了。那么接下来我们就需要去对模型数据库进行读写操作等。自然,有一个上下文用来执行/处理相关数据信息:
- [code]// 对context进行操作
- coreDataContext = [[NSManagedObjectContext alloc] init];
- [coreDataContext setPersistentStoreCoordinator:coreDataCoordinator];</span>
- 我们尝试去插入一条数据:
- [code]NSManagedObject* object = [NSEntityDescription insertNewObjectForEntityForName:@"TestCoreData" inManagedObjectContext:coreDataContext];
- [object setValue:[NSNumber numberWithInt:data->intType] forKey:@"intType"];
- [object setValue:[NSNumber numberWithFloat:data->floatType] forKey:@"floatType"];
- [object setValue:[NSNumber numberWithDouble:data->doubleType] forKey:@"doubleType"];
- [object setValue:[NSString stringWithUTF8String:data->testString] forKey:@"stringType"];
|
插入数据过程中,首先
获取一个NSManagedObject的对象,可以这么理解,NSManagedObject就代表了一条数据信息,我们用insertNewObjectForEntityForName往TestCoreData这个数据模型中
添加了一条数据:object。后面的[object setValue:"***" forkey:"***"]即为该条插入的数据进行赋值。
添加完毕数据后,内存中模型已经有
添加的数据了。但是
文件中还没有同步进去,那么我们
调用:
[coreDataContext save:nil];
即可将刚才插入的数据保存到
文件中。
关于其他
修改、
删除等操作,以及core data中的releationship操作等,可以参考附件程序或查阅相关其他资料。
【sqlite】
sqlite是一款轻量级
数据库,在c、c++、java以及其他各种产品中都有涉及到,xcode对
sqlite也提供的原生
支持。使用
sqlite也不难,只要在Link Binary With Libraries中
添加ib
sqlite3.0.dylib(我是用的3.0版本),然后在需要使用的地方
添加头
文件:#include<
sqlite3.h>即可。
sqlite的操作很方便,也很直观:
复制代码
- (IBAction)onBtn sqlite:(id)sender {
// 初始化数据库要保存的地方,如果存在则删除
NSString* strsqliteFilePath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"sqlite.sqlite"];
BOOL bIsDir = FALSE;
if ([[NSFileManager defaultManager] fileExistsAtPath:strsqliteFilePath isDirectory:&bIsDir]) {
[[NSFileManager defaultManager] removeItemAtPath:strsqliteFilePath error:nil];
}
sqlite3* sqlite = NULL;
// 首先打开数据库路径,如果不存在则创建
if (sqlITE_OK != sqlite3_open([strsqliteFilePath UTF8String],&sqlite)) {
NSLog(@"sqlite3: open error...");
// create table
// 创建表,主要就是sql语句
NSString* strCreateTable = @"CREATE TABLE TESTCOREDATA(intType INTEGER,floatType FLOAT,doubleType DOUBLE,stringType VARCHAR(256))";
if (sqlite3_exec(sqlite,[strCreateTable UTF8String],nil,nil) != sqlITE_OK) {
NSLog(@"sqlite Create table error...");
// 接下来是生成10w条测试数据
NSArray* arrayTest = [self arrayWithData:100000];
NSLog(@"Before save...");
// !!!这里很重要,将所有的insert操作作为一个transaction操作,这样避免每次insert的时候都去写文件,导致IO时间拖慢整个数据插入操作
NSString* strBegin = @"BEGIN TRANSACTION";
sqlite3_exec(sqlite,[strBegin UTF8String],NULL,NULL);
// 遍历数据并插入,就是普通的sql语句操作
for (NSValue* value in arrayTest)
{
Data* data = [value pointerValue];
NSString* strsqlInsert = [NSString stringWithFormat:@"INSERT INTO TESTCOREDATA(intType,floatType,doubleType,stringType) values(%d,%f,%lf,'%s')",data->intType,data->floatType,data->doubleType,data->testString];
if (sqlITE_OK != sqlite3_exec(sqlite,[strsqlInsert UTF8String],NULL))
const char* errormsg = sqlite3_errmsg(sqlite);
NSLog(@"exec Error...");
free(data);
// 提交所有的插入操作
NSString* strEnd = @"COMMIT";
NSLog(@"End Save...");
// 不使用的时候关闭即可
sqlite3_close(sqlite);
sqlite = NULL;
}
|
sqlite非常直观,并且依赖于
sql语句,所以
sqlite的有点在于灵活性高,上手简单并易于理解。缺点就是带来了很多底层
数据库的操作,一般都需要自己再去进行数据建模并进行封装使用。
相关读写步骤也很简单:
复制代码
- (IBAction)onBtn sqliteRead:(id)sender {
if (![[NSFileManager defaultManager] fileExistsAtPath:strsqliteFilePath isDirectory:&bIsDir]) {
NSLog(@"sqlite Open Error....File NOt exist...");
return;
sqlite))
NSLog(@"sqlite open error...");
NSString* strsql = @"select * from TESTCOREDATA";
sqlite3_stmt* stmt;
// 将对应的操作信息跟stmt进行bind,如果有相关条件可以在prepare之后进行调整
sqlite3_prepare_v2(sqlite,[strsql UTF8String],-1,&stmt,247)"> // 获取执行sql的返回结果
while (sqlITE_ROW == sqlite3_step(stmt)) {
int nIntType = sqlite3_column_int(stmt,0);
float floatType = sqlite3_column_double(stmt,1);
double doubleType = sqlite3_column_double(stmt,2);
const unsigned char* strTest = sqlite3_column_text(stmt,3);
break;
}
|
【FMDB】
fmdb是一个开源的库(
https://github.com/ccgus/fmdb),主要的操作就是针对
sqlite进行封装并提供了很多针对线程、多个
sqlite实例的管理等相关操作。避免了
sqlite操作过程中多行的各种bind,getcolumn等操作。
要使用fmdb,将
获取到的包中src下的
文件(除了fmdb.m)拖到自己工程中(最好用一个group管理起来),然后
添加lib
sqlite3.0.dylib即可。
照例我们先来看下fmdb的基础
用法:
复制代码
NSString* str sqliteFilePath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"fmdb. sqlite"];
FMDatabase* db = [FMDatabase databaseWithPath:strsqliteFilePath];
if (![db open]) {
NSLog(@"db Open Error...");
[db executeUpdate:strCreateTable];
NSLog(@"begin ");
[db beginTransaction];
[db executeUpdate:strsqlInsert];
[db commit];
NSLog(@"end...");
[db close];
db = nil;
|
fmdb使用比直接
调用sqlite要省略很多
代码和相关参数(估计让很多人很头疼),整个使用过程感觉就是四个字:酣畅淋漓。只将必要的一些关键操作需要给出参数。符合大部分对
性能要求不高的场合,非常方便。fmdb针对读取操作也一样方便,这里就不多讲。接下来我们看看fmdb中其他几个
文件的用途,有几个比较有趣的东西可以细说。
在我们拿到的src中,还有部分
文件我们在基本的使用场景中很少用到的:
FMDatabaseAdditions.*
文件,我们可以打开h
文件看下:
复制代码
/** Return `int` value for query
@param query The sql query to be performed.
@param ... A list of parameters that will be bound to the `?` placeholders in the sql query.
@return `int` value.
*/
- (int)intForQuery:(NSString*)query,...;
/** Return `long` value for query
@return `long` value.
- (long)longForQuery:(NSString*)query,...;
|
很直观是不是,就是针对某些你确定只有一个返回值的select语句,就不需要再通过返回FMResultSet并遍历set
获取了。提供简单、直观易懂的操作。
FMDatabaseQueue.*提供多线程下针对db操作的一个队列。使用FMDatabaseQueue,我们在任何线程中,都可以操作:
复制代码
[queue inDatabase:^(FMDatabase *db) {
// 操作db
}];
而由queue自己去保证执行的先后顺序和唯一性,避免同时操作时产生冲突等。
FMDatabasePool*则提供了一个db池,每一个db实力都会放在FMDatabasePool的池中,使用完成后归还db即可,sqlite相关的生命周期都由db池进行管理。避免经常性的open和close操作。
具体的相关细节可以参考fmdb的源码。
【sqlitepersistentobject】
sqlitepersistentobject库是基于ORM模型编写,将没一条数据都封装成对应的一个对象,而且完全屏蔽相关表名、文件名等信息,采用sqlitepersistentobject时,将所有的信息都屏蔽在实现细节后面。操作的时候只要操作每一个对象即可。
sqlitepersistentobject相关下载路径:https://code.google.com/p/sqlitepersistentobjects/
同样,要使用sqlitepersistentobject,首先将库下载下来后,将src中相关文件拉入到自己的工程。并添加libsqlite3.0.dylib。因为sqlitepersistentobject底层也是用的sqlite进行的操作。
先来看下基础的数据插入部分:
[code]#import <Foundation/Foundation.h>
#import "sqlitePersistentObject.h"
@interface ZJsqlitePersistentobjectsPerson : sqlitePersistentObject
int intType;
float floatType;
double doubleType;
NSString* stringType;
@property(assign,nonatomic) int intType;
@property(copy,nonatomic) NSString* stringType;
@end
|
首先我们有一个数据类,继承自
sqlitePersistentObject,每一个ZJ
sqlitePersistentobjectPerson对象都对应
数据库中的一条记录。
接下来,我们
添加一批ZJ
sqlitePersistentobjectsPerson数据:
复制代码
NSArray* arrayTest = [self arrayWithData:1000];
NSLog(@"begin...");
ZJsqlitePersistentobjectsPerson* person = [[ZJsqlitePersistentobjectsPerson alloc] init];
person.intType = data->intType;
person.floatType = data->floatType;
person.doubleType = data->doubleType;
person.stringType = [NSString stringWithUTF8String:data->testString];
[person save];
[person release];
// [ZJsqlitePersistentobjectsPerson clearCache];
[ZJsqlitePersistentobjectsPerson clearCache];
NSLog(@"end...");
|
可以从插入过程中看到,整个操作过程中完全
屏蔽了相关
sql语句、表结构等细节。非常方便简单,不过
sqlitepesistentobject只
支持基础数据类型和实现了NSCoding等相关类,由此,对于相关集合类型(NSArray,NSSet,NSDictionary)等是
不支持的。关于
sqlitepersistentobject的其他细节,可以参考
sqlitepersistentobject源码。
【总结】
综合比较coredata、
sqlite、fmdb和
sqlitepersistentobject等四种数据永久化方式,个人认为:
1、如果只是基础数据类型并且对
sql不熟悉,用
sqlitepersistenttobject是最理想的,我们需要的是数据,
sqlitepersistentobject需要你面对的也是一条一条的数据。
2、如果需要操作一般的比较复杂的
数据库以及类型,个人推荐用fmdb,非常方便和便于操作,而且
sqlite本身还
支持对数据加密的借口。
3、如果需要很好的
性能,可以使用
sqlite并自己封装相关接口。
4、如果需要基本的数据模型,并且对xcode可视化情有独钟的话,可以使用coredata->data model等。
由于对四种数据存储类型只是浅尝辄止,其中不免疏漏的地方。
原文点击打开链接
原文链接:https://www.f2er.com/sqlite/199263.html