iOS SQLite学习(上)

前言:App的开发都七七八八了(当然还有大量的bug),基本功能是差不多都有了,有些地方可能比官方的还要好上一些(写了差不多3个月,比官方烂太多还真说不过去)。但是,作为移动应用,流畅和几乎无迟滞的体验会更受到用户欢迎。我的App问题就是,每一次都要向服务器请求json数据。虽然每次的数据量都不大,但等那么一两秒,长期下来,用户肯定会感到厌烦。而且,请求数据受到网络环境影响,等待的时间可能会更长,如果请求失败,估计摔手机的心都有了(我一般会直接删除应用)。同时,这么吃流量的怪兽,估计没多少个人会在非WiFi环境下使用,用户活跃数又随之减少了。以上就是我决心做缓存的原因。

 一、前期准备
先添加开发包“libsqlite3.0.dylib”,然后在需要调用数据库的源文件加上“#import "/usr/include/sqlite3.h"”,最后一个就是起码对SQL语言有个大概的了解。我查阅了一下SQLite的一些资料,SQLite是针对嵌入设备设计的,属于轻量级的数据库,数据量不大的时候可以有相当不错的性能。对于iOS的设备而言,不可能进行大量的数据处理,毕竟CPU的性能就摆在那里。SQLite提供的是C语言的API,相信可以用于移植,同时生成的数据库也是兼容其他平台上的SQLite。不过,虽然使用C语言的API保证了效率和移植性,但是获取出来时的数据都是属于C的基本类型。对于使用Object-C的开发者来说,UI的各种控件几乎都需要用到Object-C的对象,那么就必须对这些数据做进一步的处理。还有,由于C语言的特性,所以写出来的代码也有一点臃肿(可能我的C还不够好)。此外,SQLite的数据库只有一个文件,管理起来不会很麻烦。SQLite操作的方法是,先建立一个与数据库文件链接的实例,然后再使用API来进行操作和获取返回数据。
介绍一下,特有的类型吧:
 
sqlite3     主要声明为指针,用于保存获取数库文件链接的实例,所有的操作都需要用到它,关闭数据库时会释放内存。
sqlite3_stmt  主要声明为指针,可以理解为一条编译好的SQL语句,执行完成需要手动释放,否则会内存泄露。
 
在暂时用到的就这两个类型,真的很简单。
 
二、SQLite的API
1.sqlite3_open()
最开始用的肯定是这个方法了,作用是打开数据库的文件,通过改变第二个参数来获得操作数据库的实例,第一个参数则为数据库文件的位置(UTF8编码喔),成功就返回SQLITE_OK。
 
2.sqlite3_prepare_v2()
执行SQL语句需要用到的方法之一,也有sqlite3_prepare()旧版接口,不过不建议使用。推荐全部用新版接口,保留旧版只是为了以前版本的兼容性。这个方法的作用,就写好的SQL语句编译为sqlite3_stmt类型,为下面执行做准备(此时语句并未开始执行)。调用成功的话返回SQLITE_OK。
 
3.sqlite3_step()
调用这个方法才是真正地执行了SQL语句,如果非查询语句执行成功的话是会返回SQLITE_DONE。如果执行的是查询语句,返回SQLITE_DONE就意味着下面已经没有结果显示了。
 
4.sqlite3_column_*()
sqlite3_column类的一系列方法都是用来获取查询后的数据,但是前提是你必须要知道你将会获得的数据类型。因为方法必须和数据类型相匹配,否则你将无法获取正确的数据。此外,通过第二个参数来获取,同一行数据的不同列,0是最开始的列。如果有多行结果,就要通过不断执行sqlite3_step()来获取所有行的结果,直到sqlite3_step()的返回结果是SQLITE_DONE。
 
5.sqlite3_finalize()
释放sqlite3_stmt,如果使用完sqlite3_stmt不执行释放,就会造成内存泄露。不过,这里有一点值得注意,就是sqlite3_column_*()、sqlite3_finalize()和sqlite3_stmt这三者的关系。sqlite3_column_*()获取结果,本来是和sqlite3_finalize()没什么关系的,但事实并非如此。如果sqlite3_column_*()返回的是基本类型,那么何时调用sqlite3_finalize()都没有问题,但如果是字符串之类的,那就要注意了。SQLite使用的都是C级别的API,那么C没有真正的字符串类型的特性它也继承了。意味着在获取在获取字符串的时候,它为了内存不会被意外释放掉,那么它肯定是动态申请内存空间,然后把字符串放进去,最后才返回一个字符串的首地址。既然是动态申请空间,那么肯定要由自己来释放,那么释放的任务实际上就交给了sqlite3_finalize()。所以,如果要调用sqlite3_finalize(),必须保证自己的获取的字符串有拷贝(不是指针拷贝),或者确定不会再调用了,否则提前释放sqlite3_stmt会造成无法估计的错误。
 
6.sqlite3_close()
用来关闭已经打开了的数据库,也就是有释放内存的功能,最后不调用这个方法的话也是会内存泄露的。
 
7.sqlite3_bind_*()
用来绑定数据,避免重复使用sqlite3_prepare_v2(),可以提升效率。其中类似字符串的类型,sqlite3_bind_text()等方法会多一个参数,可以传SQLITE_STATIC或者SQLITE_TRANSIENT,前者代表传入的字符串为静态,不会被意外释放。后者则表示无法保证字符串是否被释放。
 
8.sqlite3_exec()
非查询语句也可以使用这个方法,不过需要在调用后free errmsg,否则也会造成内存泄露。
 

三、SQLite的数据类型
1.NULL
就是表示空值。
2.INTEGER
表示无符号整数。
3.REAL
表示浮点数
4.TEXT
字符串类型,支持UTF8编码。
5.BLOB
二进制类型。
 
此外SQLite没有专门的布尔类型和时间类型,布尔类型可以直接用整数类型代替,时间类型则可以用TEXT、REAL或者INTEGER。不过,在查询的时候,如果储存为时间,就可以使用更合适的时间比较,方便查询。其实时间类型我也暂时没有使用到,因为我的数据不需要直接使用SQL语句来比较时间,所以我只是直接储存为TEXT类型。
 
四、使用例子
打开数据库
1 sqlite3 *dataBase = NULL;
2 NSArray *documentsPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
3 NSString *databaseFilePath = [[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:cache.sqlitedb];
4     
5 if (sqlite3_open([databaseFilePath UTF8String], &dataBase)==SQLITE_OK)
6         NSLog(@"open sqlite db ok.");

 

建立数据表

 1 const char *createCacheList = "CREATE TABLE IF NOT EXISTS "CACHELISTTABLE" (id TEXT PRIMARY KEY, cachedate TEXT)";
 2 sqlite3_stmt *createCacheListStmt = NULL;
 3 
 4 if (sqlite3_prepare_v2(dataBase, createCacheList, strlen(createCacheList), &createCacheListStmt, NULL) != SQLITE_OK) {
 5         
 6     if (createCacheListStmt)
 7         sqlite3_finalize(createCacheListStmt);
 8     return nil;
 9    
10 }
11 if (sqlite3_step(createCacheListStmt) != SQLITE_DONE) {
12         
13     sqlite3_finalize(createCacheListStmt);
14     return nil;
15         
16 }
17 sqlite3_finalize(createCacheListStmt);

上面的例子都有做了异常处理,实际上,当你的数据来源是可预知,系统的文件系统是稳定可靠的话,可以省去上面的异常处理。省去异常处理的话,代码会更加简短、可读性更好,但是出现错误的时候更难找到出错的地方。

暂时就先写这么多,还有进阶的内容下次继续写。

 
 
 
posted @ 2012-11-01 11:48  Pinka  阅读(818)  评论(0编辑  收藏  举报