IOS 数据持久化的各种方式
1.plist文件存储
每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其他文件系统隔离。应用必须待在自己的沙盒里,其他应用不能访问该沙盒
应用沙盒的文件系统目录,如下图所示(假设应用的名称叫Layer)
模拟器应用沙盒的根路径在: (apple是用户名, 6.0是模拟器版本)
/Users/apple/Library/Application Support/iPhone Simulator/6.0/Applications
Document 保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录
temp 保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录
Library/Caches 保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据
Library/Preference: 保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录
下面是示例代码:
1/** 保存数据*/ 2- (IBAction)saveBtn 3{ 4NSLog(@"保存"); 5//1.获取沙盒根路径 6NSString *homePath = NSHomeDirectory(); 7//2.document路径 8NSString *path = [homePath stringByAppendingPathComponent:@"Documents"]; 9//3.新建数据10NSArray *array = [NSArray arrayWithObjects:@"nan",@(22), nil];11NSDictionary *dict = @{@"sss":@"sddd",@"ssssaw":@(1222)};121314//4.存储数据15NSString *fullPath1 = [path stringByAppendingPathComponent:@"data1.plist"];16NSString *fullPath2 = [path stringByAppendingPathComponent:@"data2.plist"];1718[array writeToFile:fullPath1 atomically:YES];//数组写入plist19 [dict writeToFile:fullPath2 atomically:YES];2021NSLog(@"dict - %@",dict);22}2324/** 读取数据*/25- (IBAction)readBtn26{27//1.获取home目录28NSString *home = NSHomeDirectory();29//2.拼接Document目录30NSString *path = [home stringByAppendingPathComponent:@"Documents"];31//3.文件路径32NSString *filePath1 = [path stringByAppendingPathComponent:@"data1.plist"];33NSString *filePath2 = [path stringByAppendingPathComponent:@"data2.plist"];34//读取文件35NSArray *array = [NSArray arrayWithContentsOfFile:filePath1];36NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath2];37NSLog(@"array - %@, dict - %@",array , dict);38}
缺点:只能存储含有 writeToFile:方法的对象,如NSDictionary,NSArray等.
2.偏好设置 -- 存放目录 Library/Preference:
很多iOS应用都支持偏好设置,比如保存用户名、密码、字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能
每个应用都有个NSUserDefaults实例,通过它来存取偏好设置
比如,保存用户名、字体大小、是否自动登录
下面是示例代码
/** 保存数据*/- (IBAction)save
{
// 1.利用NSUserDefaults,就能直接访问软件的偏好设置(Library/Preferences)是个单例对象NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//2.存储数据[defaults setObject:@"sd"forKey:@"user"];//其中要保存的数据是setObject:@"sd",@“sd”是数据,forKey:@"user"是以user的键值保存的,
/**
key - value
@"user" -@"sd"
*/[defaults setObject:@"123w"forKey:@"test"];
[defaults setInteger:20forKey:@"age"];
[defaults setBool:YES forKey:@"auto_login"];
//3.立刻同步(相当于更新数据) [defaults synchronize];
}/** 读取数据*/- (IBAction)read
{
//1.建立NSUserDefaults对象NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//2.读取数据NSString *user = [defaults objectForKey:@"user"];
NSString *test = [defaults objectForKey:@"test"];
NSInteger age = [defaults integerForKey:@"age"];
BOOL autoLogin = [defaults boolForKey:@"auto_login"];
NSLog(@"user - %@\n test - %@\n age - %d\n autoLogin - %d\n,",user,test,age,autoLogin);}
//下面是解档归档基本OC对象
- (void)test
{
NSString *mName = @"zhangsan";
// 获取doc的目录
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 拼接保存的路径
NSString *filePath = [docPath stringByAppendingPathComponent:@"dataList"];
// NSData *mData = [mName dataUsingEncoding:NSUTF8StringEncoding];
[NSKeyedArchiver archiveRootObject:mName toFile:filePath];
NSString *testName = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"解档出来的数据是 ----- %@",testName);
}
注意:UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法强制写入[defaults synchornize];
缺点:本质还是plist文件存储,相对于plist文件存储来讲存储数据更快捷.
3.NSKeyedArchiver(NSCoding)
如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,可以直接用NSKeyedArchiver进行归档和恢复
不是所有的对象都可以直接用这种方法进行归档,只有遵守了NSCoding协议的对象才可以
NSCoding协议有2个方法:
encodeWithCoder:
每次归档对象时,都会调用这个方法。一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:方法归档实例变量
initWithCoder:
每次从文件中恢复(解码)对象时,都会调用这个方法。一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey方法解码实例变量
示例代码
1#import 2 3@interfacePerson : NSObject 4 5@property (nonatomic , copy) NSString *name; 6@property (nonatomic , assign)int age; 7@property (nonatomic , assign)double height; 8 9@end101112#import"Person.h"1314@implementation Person1516//归档的时候调用17/** 将某个对象写入文件的时候会调用,在这个方法中说明哪些对象的哪些属性需要存储*/18- (void)encodeWithCoder:(NSCoder *)enCoder19{20NSLog(@"enCoder - %@",enCoder);21[enCoder encodeObject:self.name forKey:@"name"];22[enCoder encodeInt:self.age forKey:@"age"];23[enCoder encodeDouble:self.height forKey:@"height"];2425}2627/** 解档时候调用,在这个方法中说清楚哪些属性要解档*/28- (id)initWithCoder:(NSCoder *)decoder29{30if(self = [super init])31 {32//读取文件内容33self.name = [decoder decodeObjectForKey:@"name"];34self.age = [decoder decodeIntForKey:@"age"];35self.height = [decoder decodeDoubleForKey:@"height"];3637 }3839return self;40}414243@end444546#import"SDViewController.h"47#import"Person.h"4849@interface SDViewController ()5051@end5253@implementation SDViewController5455- (void)viewDidLoad56{57 [super viewDidLoad];585960}6162- (IBAction)save63{64//1.数据对象65Person *p1 = [[Person alloc] init];66p1.name =@"王麻子";67p1.age =20;68p1.height =1.98;69Person *p2 = [[Person alloc] init];70p2.name =@"李四";71p2.age =56;72p2.height =1.68;73//2.归档数据对象74//2.1获得文件的Documents全路径75NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];76//2.2获得文件的全路径77NSString *path = [doc stringByAppendingPathComponent:@"person.txt"];78//2.3将对象归档79 [NSKeyedArchiver archiveRootObject:p1 toFile:path];8081}82- (IBAction)read83{84// 1.获得Documents的全路径85NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];86// 2.获得文件的全路径87NSString *path = [doc stringByAppendingPathComponent:@"person.txt"];88// 3.从文件中读取Person对象89Person *p3 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];9091NSLog(@"%@ %d %f", p3.name, p3.age, p3.height);9293}949596@end
NSKeyedArchiver-归档对象的注意:
如果父类也遵守了NSCoding协议,请注意:应该在encodeWithCoder:方法中加上一句[super encodeWithCode:encode];确保继承的实例变量也能被编码,
即也能被归档应该在initWithCoder:方法中加上一句self = [super initWithCoder:decoder];确保继承的实例变量也能被解码,即也能被恢复
但有时候可能想将多个对象写入到同一个文件中,那么就要使用NSData来进行归档对象NSData可以为一些数据提供临时存储空间,以便随后写入文件,或者存放从磁盘读取的文件内容。
示例代码
1Person.h里面 2 3#import 4 5@interfacePerson : NSObject 6 7@property (nonatomic , copy) NSString *name; 8@property (nonatomic , assign)int age; 9@property (nonatomic , assign)double height; 10 11@end 12 13Person.m里面 14#import"Person.h" 15 16@implementation Person 17 18//归档的时候调用 19/** 将某个对象写入文件的时候会调用,在这个方法中说明哪些对象的哪些属性需要存储*/ 20- (void)encodeWithCoder:(NSCoder *)enCoder 21{ 22NSLog(@"enCoder - %@",enCoder); 23[enCoder encodeObject:self.name forKey:@"name"]; 24[enCoder encodeInt:self.age forKey:@"age"]; 25[enCoder encodeDouble:self.height forKey:@"height"]; 26 27} 28 29/** 解档时候调用,在这个方法中说清楚哪些属性要解档*/ 30- (id)initWithCoder:(NSCoder *)decoder 31{ 32if(self = [super init]) 33 { 34//读取文件内容 35self.name = [decoder decodeObjectForKey:@"name"]; 36self.age = [decoder decodeIntForKey:@"age"]; 37self.height = [decoder decodeDoubleForKey:@"height"]; 38 39 } 40 41return self; 42} 43 44 45@end 46 47SDViewController.m里面 48 49#import"SDViewController.h" 50#import"Person.h" 51 52@interface SDViewController () 53 54@end 55 56@implementation SDViewController 57 58- (void)viewDidLoad 59{ 60 [super viewDidLoad]; 61// Do any additional setup after loading the view, typically from a nib. 62 63} 64 65- (void)didReceiveMemoryWarning 66{ 67 [super didReceiveMemoryWarning]; 68// Dispose of any resources that can be recreated. 69} 70 71 72- (IBAction)save 73{ 74//1.数据对象 75Person *p1 = [[Person alloc] init]; 76p1.name =@"王麻子"; 77p1.age =20; 78p1.height =1.98; 79Person *p2 = [[Person alloc] init]; 80p2.name =@"李四"; 81p2.age =56; 82p2.height =1.68; 83//2.归档数据对象 84//2.1获得文件的Documents全路径 85NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; 86//2.2获得文件的全路径 87NSString *path = [doc stringByAppendingPathComponent:@"person.txt"]; 88////2.3将对象归档 89// [NSKeyedArchiver archiveRootObject:p1 toFile:path]; 90// 91// 新建一块可变数据区 92NSMutableData *data = [NSMutableData data]; 93// 将数据区连接到一个NSKeyedArchiver对象 94NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; 95[archiver encodeObject:p1 forKey:@"person1"]; 96[archiver encodeObject:p2 forKey:@"person2"]; 97// 存档完毕(一定要调用这个方法) 98 [archiver finishEncoding]; 99//将存档的数据写入文件100 [data writeToFile:path atomically:YES];101102}103- (IBAction)read104{105// 1.获得Documents的全路径106NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];107// 2.获得文件的全路径108NSString *path = [doc stringByAppendingPathComponent:@"person.txt"];109// 3.从文件中读取Student对象110// Person *p3 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];111NSData *data = [NSData dataWithContentsOfFile:path];112113// 根据数据,解析成一个NSKeyedUnarchiver对象114NSKeyedUnarchiver *unchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];115Person *p1 = [unchiver decodeObjectForKey:@"person1"];116Person *p2 = [unchiver decodeObjectForKey:@"person2"];117 [unchiver finishDecoding];118NSLog(@"%@ %d %f", p1.name, p1.age, p1.height);119NSLog(@"%@ %d %f", p2.name, p2.age, p2.height);120121}122123124125@end
NSCoder.h
- (void)encodeValueOfObjCType:(const char *)type at:(const void *)addr; //解档C类型数据,addr传入地址
- (void)encodeDataObject:(NSData *)data; //归档一个NSData型对象
- (void)decodeValueOfObjCType:(const char *)type at:(void *)data; ////解档C类型数据
- (NSData *)decodeDataObject;//解档一个NSData型对象
@end
@interface NSCoder (NSExtendedCoder)
/** 归档相关函数*/
- (void)encodeObject:(id)object;
- (void)encodeRootObject:(id)rootObject;
- (void)encodeBycopyObject:(id)anObject;
- (void)encodeByrefObject:(id)anObject;
- (void)encodeConditionalObject:(id)object;
- (void)encodeValuesOfObjCTypes:(const char *)types, ...;
- (void)encodeArrayOfObjCType:(const char *)type count:(NSUInteger)count at:(const void *)array;
- (void)encodeBytes:(const void *)byteaddr length:(NSUInteger)length;
/** 解档相关函数*/
- (id)decodeObject;
- (void)decodeValuesOfObjCTypes:(const char *)types, ...;
- (void)decodeArrayOfObjCType:(const char *)itemType count:(NSUInteger)count at:(void *)array;
- (void *)decodeBytesWithReturnedLength:(NSUInteger *)lengthp NS_RETURNS_INNER_POINTER;
- (unsigned)systemVersion;
//是否遵循KeyedCoding
- (BOOL)allowsKeyedCoding;
/** 归档相关函数*/
- (void)encodeObject:(id)objv forKey:(NSString *)key;
- (void)encodeConditionalObject:(id)objv forKey:(NSString *)key;
- (void)encodeBool:(BOOL)boolv forKey:(NSString *)key;
- (void)encodeInt:(int)intv forKey:(NSString *)key;
- (void)encodeInt32:(int32_t)intv forKey:(NSString *)key;
- (void)encodeInt64:(int64_t)intv forKey:(NSString *)key;
- (void)encodeFloat:(float)realv forKey:(NSString *)key;
- (void)encodeDouble:(double)realv forKey:(NSString *)key;
- (void)encodeBytes:(const uint8_t *)bytesp length:(NSUInteger)lenv forKey:(NSString *)key;
/** 解档相关函数*/
- (BOOL)containsValueForKey:(NSString *)key;
- (id)decodeObjectForKey:(NSString *)key;
- (BOOL)decodeBoolForKey:(NSString *)key;
- (int)decodeIntForKey:(NSString *)key;
- (int32_t)decodeInt32ForKey:(NSString *)key;
- (int64_t)decodeInt64ForKey:(NSString *)key;
- (float)decodeFloatForKey:(NSString *)key;
- (double)decodeDoubleForKey:(NSString *)key;
- (const uint8_t *)decodeBytesForKey:(NSString *)key returnedLength:(NSUInteger *)lengthp NS_RETURNS_INNER_POINTER; // returned bytes immutable!
- (void)encodeInteger:(NSInteger)intv forKey:(NSString *)key NS_AVAILABLE(10_5, 2_0);
- (NSInteger)decodeIntegerForKey:(NSString *)key NS_AVAILABLE(10_5, 2_0);
// Returns YES if this coder requires secure coding. Secure coders check a list of allowed classes before decoding objects, and all objects must implement NSSecureCoding.
//是否安全解档
- (BOOL)requiresSecureCoding NS_AVAILABLE(10_8, 6_0);
SQLite3
SQLite3是一款开源的嵌入式关系型数据库,可移植性好、易使用、内存开销小.SQLite3是无类型的,意味着你可以保存任何类型的数据到任意表的任意字段中。
数据库语句
/*简单约束*/
//如果表不存在就创建一张t_student的表(id为主键,自动增长,name text类型,age integer类型);
CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER);
//如果表不存在就创建一张t_student的表(id为主键,自动增长,name text类型不能为空,age integer类型不能为空);
CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL);
//如果表不存在就创建一张t_student的表(id为主键,自动增长,name text类型,并且每一个是唯一的,age integer类型不能为空);
CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER);
//如果表不存在就创建一张t_student的表(id为主键,自动增长,name text类型,age integer类型默认为1);
CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER DEFAULT 1);
/*分页*/
//先将表按照升序排,跳过最前面30条语句,然后取10条记录
SELECT * FROM t_student ORDER BY id ASC LIMIT 30, 10;
/*排序*/
//取出表中score > 50的数据并按照降序排列
SELECT * FROM t_student WHERE score > 50 ORDER BY age DESC;
//取出表中score<50的数据并按照升序排列,score>50的数据按照降序排列
SELECT * FROM t_student WHERE score < 50 ORDER BY age ASC , score DESC;
/*计量*/
//统计表中age > 50的个数
SELECT COUNT(*) FROM t_student WHERE age > 50;
/*别名*/
//将name 命名为 myName, age 命名为 myAge, score 命名为myScore
SELECT name as myName, age as myAge, score as myScore FROM t_student;
//将name 命名为 myName, age 命名为 myAge, score 命名为myScore
SELECT name myName, age myAge, score myScore FROM t_student;
//给t_student表起个别名叫做s,利用s来引用表中的字段,取出age > 50 的数据,将name 命名为 myName, age 命名为 myAge, score 命名为myScore
SELECT s.name myName, s.age myAge, s.score myScore FROM t_student s WHERE s.age > 50;
/*查询*/
//从表中查询name,age,score
SELECT name, age, score FROM t_student;
//查询整张表
SELECT * FROM t_student;
/*修改指定数据*/
//从表中取出age = 10 的那条数据,将name 字段值设为MM
UPDATE t_student SET name = 'MM' WHERE age = 10;
/从表中取出age = 7 的那条数据,将name 字段值设为WW
UPDATE t_student SET name = 'WW' WHERE age is 7;
//取出表中age < 20 的数据,并将name 全部设置为XXOO
UPDATE t_student SET name = 'XXOO' WHERE age < 20;
//取出表中age < 50 并且 score > 10的数据,将满足条件的每一条数据中的name字段设置为NNMM
UPDATE t_student SET name = 'NNMM' WHERE age < 50 and score > 10;
/*删除数据*/
//删除表中的数据
DELETE FROM t_student;
/*更新数据*/
//将表中的name字段全部设置为MM
UPDATE t_student SET name = 'MM';
/*插入数据*/
//往表中插入一条数据(name = jonathan , age = 28, score = 100)
INSERT INTO t_student(age, score, name) VALUES ('28', 100, 'jonathan');
//往表中插入一条数据(name = lee , age = 28)
INSERT INTO t_student(name, age) VALUES ('lee', '28');
//往表中插入一条数据(score = 100)
INSERT INTO t_student(score) VALUES (100);
/*添加主键*/
//如果表不存在就创建一张表,id为主键自动增长,integer类型,name 为text类型,age 为integer类型,score为浮点类型
CREATE TABLE IF NOT EXISTS t_student (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, score REAL);
/*删除表*/
//销毁t_student表
DROP TABLE t_student;
//如果表存在就销毁这张表
DROP TABLE IF EXISTS t_student;
/****************************************************** 应 用 *****************************************************************/
- (void)viewDidLoad
{
[super viewDidLoad];
// 1.打开创建一个数据库
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [path stringByAppendingPathComponent:@"t_student.sqlite"];
int result = sqlite3_open(fileName.UTF8String, &_db);
if (result == SQLITE_OK) {
// 创建表
const char *sql = "CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , name TEXT NOT NULL, age INTERGER NOT NULL);";
char *error = nil;
sqlite3_exec(self.db, sql, NULL, NULL, &error);
if (error) {
NSLog(@"创建失败");
}else{
NSLog(@"创建成功");
}
}else{
NSLog(@"失败");
}
}
/**
* 增加
*/
- (IBAction)insertBtnClick:(id)sender {
for (int i = 0; i < 100; i++) {
NSString *name = [NSString stringWithFormat:@"lee-%d", i];
int age = arc4random_uniform(50) + 50;
NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_student(name, age) VALUES ('%@', %d);", name, age];
char *error = nil;
sqlite3_exec(self.db, sql.UTF8String, NULL, NULL, &error);
if (error) {
NSLog(@"创建失败");
}else{
NSLog(@"创建成功");
}
}
}
/**
* 更新
*/
- (IBAction)updateBtnClick:(id)sender {
const char *sql = "UPDATE t_student SET name = 'DG';";
char *error = nil;
sqlite3_exec(self.db, sql, NULL, NULL, &error);
if (error) {
NSLog(@"更新失败");
}else{
NSLog(@"更新成功");
}
}
/**
* 删除
*/
- (IBAction)deleteBtnClick:(id)sender {
const char *sql = "DELETE FROM t_student;";
char *error = nil;
sqlite3_exec(self.db, sql, NULL, NULL, &error);
if (error) {
NSLog(@"删除失败");
}else{
NSLog(@"删除成功");
}
}
/**
* 查询
*/
- (IBAction)selectBtnClick:(id)sender {
const char *zSql = "SELECT * FROM t_student";
sqlite3_stmt *stmt;
// 查询前的准备, 检查sql语句是否正确
int result = sqlite3_prepare_v2(self.db, zSql, -1, &stmt, NULL);
if(result == SQLITE_OK) { // 准备完成,没有错误
// 提取查询到得数据到stmt, 一次提取一条
while(sqlite3_step(stmt) == SQLITE_ROW){
// 取出提取到得记录(数据)中的第0列数据和第一列数据
const unsigned char *name = sqlite3_column_text(stmt, 0);
int age = sqlite3_column_int(stmt, 1);
NSLog(@"%s, %d", name, age);
}
}
}
SqLite3小结
1.打开数据库
int sqlite3_open(
const char *filename, // 数据库的文件路径
sqlite3 **.ppDb // 数据库实例
);
2.执行任何SQL语句
int sqlite3_exec(
sqlite3*, // 一个打开的数据库实例
const char *sql, // 需要执行的SQL语句
int (*callback)(void*,int,char**,char**), // SQL语句执行完毕后的回调
void *, // 回调函数的第1个参数
char **errmsg // 错误信息
);
3.检查SQL语句的合法性(查询前的准备)
int sqlite3_prepare_v2(
sqlite3 *db, // 数据库实例
const char *zSql, // 需要检查的SQL语句
int nByte, // SQL语句的最大字节长度
sqlite3_stmt **ppStmt, // sqlite3_stmt实例,用来获得数据库数据
const char **pzTail
);
4.查询一行数据
int sqlite3_step(sqlite3_stmt*); // 如果查询到一行数据,就会返回SQLITE_ROW
5.利用stmt获得某一字段的值(字段的下标从0开始)
double sqlite3_column_double(sqlite3_stmt*, int iCol); // 浮点数据
int sqlite3_column_int(sqlite3_stmt*, int iCol); // 整型数据
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); // 长整型数据
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); // 二进制文本数据
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); // 字符串数据
6.SqLite3第三方框架FMDB使用小结
FMDB是iOS平台的SQLite数据库框架,FMDB以OC的方式封装了SQLite的C语言API.
FMDB的优点
@1使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码
@2对比苹果自带的Core Data框架,更加轻量级和灵活
@3提供了多线程安全的数据库操作方法,有效地防止数据混乱
FMDB简单使用示例代码
#import"CZViewController.h"#import"FMDB.h"@interface CZViewController ()- (IBAction)insertBtnClick;- (IBAction)updateBtnClick;- (IBAction)deleteBtnClick;- (IBAction)queryBtnClick;
@property (nonatomic, strong) FMDatabase *db;@end@implementation CZViewController- (void)viewDidLoad
{
[super viewDidLoad];
// 0.获取沙盒路径NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *fileName = [path stringByAppendingPathComponent:@"t_student.sqlite"];
// 1.获得数据库对象self.db = [FMDatabase databaseWithPath:fileName];
// 2.打开数据库if ([self.db open]) {
NSLog(@"打开成功");
// 2.1创建表BOOL success = [self.db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_student (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, age INTEGER NOT NULL);"];
if (success) {
NSLog(@"创建表成功");
}else {
NSLog(@"创建表失败");
}
}else {
NSLog(@"打开失败");
}
}- (IBAction)insertBtnClick
{
for(inti =0; i <100; i++) {
NSString *name = [NSString stringWithFormat:@"Jonathan-%d", i];
intage = arc4random_uniform(1000);
BOOL success = [self.db executeUpdate:@"INSERT INTO t_student(name , age) VALUES(?, ?);", name, @(age)];// 注意只能拼接对象类型if (success) {
NSLog(@"添加成功");
}else {
NSLog(@"添加失败");
}
}
}- (IBAction)updateBtnClick
{
BOOL success = [self.db executeUpdate:@"UPDATE t_student SET name = 'JACK' WHERE age < 50"];
if (success) {
NSLog(@"修改成功");
}else {
NSLog(@"修改失败");
}
}- (IBAction)deleteBtnClick
{
BOOL success = [self.db executeUpdate:@"DELETE FROM t_student WHERE age < ?;", @50];
if (success) {
NSLog(@"删除成功");
}else {
NSLog(@"删除失败");
}
}- (IBAction)queryBtnClick
{
// 1.查询// FMResultSet *set = [self.db executeQuery:@"SELECT id, name, age FROM t_student;"];FMResultSet *set= [self.db executeQuery:@"SELECT * FROM t_student;"];
// 2.取出数据while([set next]) {
// 取出姓名// NSString *name = [set stringForColumnIndex:1];
// 取出年龄// int age = [set intForColumnIndex:2];NSString *name = [setstringForColumn:@"name"];
intage = [setintForColumn:@"age"];
NSLog(@"name = %@, age = %d", name, age);
}
}@end
FMDB线程安全
#import"CZViewController.h"#import"FMDB.h"@interface CZViewController ()- (IBAction)insertBtnClick;- (IBAction)updateBtnClick;- (IBAction)deleteBtnClick;- (IBAction)queryBtnClick;
@property (nonatomic, strong) FMDatabase *db;
@property (nonatomic, strong) FMDatabaseQueue *queue;@end@implementation CZViewController- (void)viewDidLoad
{
[super viewDidLoad];
// 0.获取沙盒路径NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
NSString *fileName = [path stringByAppendingPathComponent:@"t_student.sqlite"];
// 1.获得数据库对象// self.db = [FMDatabase databaseWithPath:fileName];
// 1.活的数据库队列对象self.queue = [FMDatabaseQueue databaseQueueWithPath:fileName];
// 2.在数据库队列中执行线程安全的操作[self.queue inDatabase:^(FMDatabase *db) {
if ([db open]) {
// 2.1创建表BOOL success = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, money INTEGER NOT NULL);"];
if (success) {
NSLog(@"创建表成功");
}else {
NSLog(@"创建表失败");
}
}
}];
}- (IBAction)insertBtnClick
{
[self.queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO t_person(name, money) VALUES('Zs', 1000)"];
[db executeUpdate:@"INSERT INTO t_person(name, money) VALUES('LS', 1000)"];
}];
}- (IBAction)updateBtnClick
{
/* [self.queue inDatabase:^(FMDatabase *db) {
[db beginTransaction]; // 开启事务
[db executeUpdate:@"update t_person set money = 0 where name = 'Zs';"];
// [db rollback];// 主动回滚
[db executeUpdate:@"update t_person set money = 2000 where name = 'LS';"];
[db commit];// 提交事务
}];
*/
[self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"update t_person set money = 0 where name = 'Zs';"];
NSLog(@"主动回滚");
*rollback = YES;
[db executeUpdate:@"update t_person set money = 2000 where name = 'LS';"];
}];
}- (IBAction)deleteBtnClick
{
[self.queue inDatabase:^(FMDatabase *db) {
BOOL success = [db executeUpdate:@"DELETE FROM t_student WHERE age < ?;", @50];
if (success) {
NSLog(@"删除成功");
}else {
NSLog(@"删除失败");
}
}];
}- (IBAction)queryBtnClick
{
[self.queue inDatabase:^(FMDatabase *db) {
// 1.查询FMResultSet *set= [db executeQuery:@"SELECT * FROM t_student;"];
// 2.取出数据while([set next]) {
NSString *name = [setstringForColumn:@"name"];
intage = [setintForColumn:@"age"];
NSLog(@"name = %@, age = %d", name, age);
}
}];
}@end