IOS 数据持久化

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,392评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,258评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,417评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,992评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,930评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,199评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,652评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,327评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,463评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,382评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,432评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,118评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,704评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,787评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,999评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,476评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,057评论 2 341

推荐阅读更多精彩内容