一、iOS常见的几种数据存储

1.plist列表(writeToFile:)

2.偏好设置(NSUserDefaults)

3.归档(NSCodeing、NSKeyedArchiver)

4.SQLite3(数据库、一般借助第三方库FMDB)

5.Core Data(对象型的数据库)

 

二、应用沙盒

1.简介

  每一个应用都有自己的应用沙盒,也就是系统文件目录。每一个应用都必须呆在自己的应用沙盒中,不可以访问别的应用沙盒(iOS8已经允许访问其他应用沙盒)

2.结构

  

ios 档案 iOS档案库_#pragma

  应用沙盒下面一级文件夹有三个:Documents、Library、tmp;Library文件夹下有二级文件有两个:Caches、Preferences

  1)Documents:保存应⽤运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录

  2)Library/Caches:保存应用运行时⽣成的需要持久化的数据,iTunes同步设备时不会备份该目录(一般存储体积大、不需要备份的非重要数据)

  3)Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置) 应⽤会在该⺫录中查找应⽤的设置信息。iTunes同步设备时会备份该目录 

  4)tmp:保存应⽤运行时所需的临时数据,使⽤完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时 不会备份该目录 

3.应用沙盒目录的获取方法

1     // 1.获取沙盒根目录
 2     NSString *homePath = NSHomeDirectory();
 3     
 4     // 2.获取Documents目录
 5     // 第一种方法:根目录直接拼接(不建议采用,原因是如果苹果公司将文件夹名称改了,那就会很麻烦)
 6     NSString *docPath1 = [homePath stringByAppendingPathComponent:@"Documents"];
 7     // 第二种方法:方法获取(推荐使用)
 8     // 参数1:NSDocumentDirectory  查找Documents文件夹
 9     // 参数2:NSUserDomainMask     在用户目录下查找
10     // 参数3:YES                  是否展开用户目录(NO就是~,YES就是展开)
11     NSString *docPath2 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
12     
13     // 3.拼接文件路径
14     // 假设Documents文件夹下面存储一张图片image.png
15     NSString *filePath = [docPath2 stringByAppendingPathComponent:@"image.png"];

 

三、plist列表

1.简介  

  1)plist列表只能存储一些系统自定义的数据类型,例如:NSString、NSArray、NSNumber、NSDictionary、NSData等等

  2)plist列表不能存储一些我们自定义的对象类型,例如:自定义的Person对象

  3)我们一般将plist文件保存在沙河目录Documents文件夹下,文件后缀名为.plist

2.实际应用

1 #pragma mark - 数据保存
 2 - (void)saveData
 3 {
 4     // 1.获取Documents目录
 5     NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
 6     
 7     // 2.拼接文件路径
 8     // 数组形式保存文件名:user1.plist
 9     // 字典形式保存文件名:user2.plist
10     NSString *filePath1 = [docPath stringByAppendingPathComponent:@"user1.plist"];
11     NSString *filePath2 = [docPath stringByAppendingPathComponent:@"user2.plist"];
12     
13     // 3.保存数据
14     // 第一种情况:数组形式保存
15     NSArray *userArr = @[@"Frank", @25, @"M", @"湖北黄冈"];
16     [userArr writeToFile:filePath1 atomically:YES];
17     // 第二种情况:字典形式保存
18     NSDictionary *userDic = @{@"name": @"Frank", @"age": @25, @"sex": @"M", @"home": @"湖北黄冈"};
19     [userDic writeToFile:filePath2 atomically:YES];
20 }
21 
22 #pragma mark - 读取数据
23 - (void)readData
24 {
25     // 1.获取Documents目录
26     NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
27     
28     // 2.拼接文件路径
29     // 数组形式保存文件名:user1.plist
30     // 字典形式保存文件名:user2.plist
31     NSString *filePath1 = [docPath stringByAppendingPathComponent:@"user1.plist"];
32     NSString *filePath2 = [docPath stringByAppendingPathComponent:@"user2.plist"];
33     
34     // 3.读取数据
35     // 第一种情况:数组形式
36     NSArray *userArr = [NSArray arrayWithContentsOfFile:filePath1];
37     NSLog(@"userArr = %@", userArr);
38     // 第二种情况:字典形式
39     NSDictionary *userDic = [NSDictionary dictionaryWithContentsOfFile:filePath2];
40     NSLog(@"userDic = %@", userDic);
41 }

 

四、偏好设置

1.简介

  1)很多应用都支持偏好设置,iOS提供了一套标准的解决方案来为应用加入偏好设置。

  2)每一个应用都有一个实例NSUserDefault,iOS就是通过它来存储偏好设置

  3)文件存储在沙河目录Library下文件夹Preferences,文件后缀名为.plist

  4)注意我们的偏好设置是将所有的东西都保存在同一个文件中,且主要用来保存应用的一些设置信息

  5)偏好设置同plist列表一样只能保存iOS系统自定义的一些数据类型

2.实际应用

1 #pragma mark - 数据保存
 2 - (void)saveData
 3 {
 4     // 1.获取NSUserDefaults对象
 5     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
 6     
 7     // 2.保存数据
 8     [defaults setObject:@"Frank" forKey:@"name"];
 9     [defaults setInteger:25 forKey:@"age"];
10     [defaults setDouble:1.69 forKey:@"height"];
11     [defaults setFloat:148.2f forKey:@"weight"];
12     [defaults setObject:@"M" forKey:@"sex"];
13     
14     // 3.同步数据(强制数据立即保存) -- 有点类似我们刷新表
15     // 若没有同步数据,系统会在将来某一时间点自动将数据保存到Preferences文件夹下
16     [defaults synchronize];
17 }
18 
19 #pragma mark - 读取数据
20 - (void)readData
21 {
22     // 1.获取NSUserDefaults对象
23     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
24     
25     // 2.读取保存的数据
26     NSString *name = [defaults objectForKey:@"name"];
27     NSInteger age = [defaults integerForKey:@"age"];
28     double height = [defaults doubleForKey:@"height"];
29     CGFloat weight = [defaults floatForKey:@"weight"];
30     NSString *sex = [defaults objectForKey:@"sex"];
31     
32     // 3.打印输出数据
33     NSLog(@"name=%@, age=%ld, height=%.2f, weight=%.2f, sex=%@", name, age, height, weight, sex);
34 }

 

五、NSKeydeArchiver归档

1.简介

  1)与前面两中数据保存方法均不能针对自定义对象类型,归档是专门针对自定义对象类型的数据保存

  2)我们一般将归档文件保存在沙盒目录下的Documents文件夹下,其文件后缀名可以是任意的

  3)通过plist存储的文件在文件中打开是直接显示的,而归档存储的文件在文件打开时是乱码的,更加安全

2.实际应用

  1)自定义对象:UserModel类

1 // UserModel.h文件
 2 #import <Foundation/Foundation.h>
 3 
 4 #pragma mark - 归档对象必须遵循<NSCoding>协议
 5 @interface UserModel : NSObject<NSCoding>
 6 
 7 @property (nonatomic, copy) NSString *userId;      // 用户ID
 8 @property (nonatomic, copy) NSString *userName;    // 用户名
 9 @property (nonatomic, copy) NSString *password;    // 用户密码
10 @property (nonatomic, assign) NSInteger age;       // 用户的年龄
11 @property (nonatomic, assign) double weight;       // 用户的体重
12 @property (nonatomic, assign) float height;        // 用户的身高
13 @property (nonatomic, copy) NSString *userTel;     // 用户的电话号码
14 @property (nonatomic, copy) NSString *userEmail;   // 用户的邮箱
15 
16 @end
17 
18 
19 // UserModel.m文件
20 #import "UserModel.h"
21 
22 @implementation UserModel
23 
24 #pragma mark - 必须实现的协议方法<NSCoding>
25 // 1.归档
26 - (void)encodeWithCoder:(NSCoder *)aCoder
27 {
28     [aCoder encodeObject:_userId forKey:@"userId"];        // 用户ID
29     [aCoder encodeObject:_userName forKey:@"userName"];    // 用户名
30     [aCoder encodeObject:_password forKey:@"password"];    // 用户密码
31     [aCoder encodeInteger:_age forKey:@"age"];             // 用户的年龄
32     [aCoder encodeDouble:_weight forKey:@"weight"];        // 用户的体重
33     [aCoder encodeFloat:_height forKey:@"height"];         // 用户的身高
34     [aCoder encodeObject:_userTel forKey:@"userTel"];      // 用户的电话号码
35     [aCoder encodeObject:_userEmail forKey:@"userEmail"];  // 用户的邮箱
36 }
37 // 2.解档
38 - (id)initWithCoder:(NSCoder *)aDecoder
39 {
40     if (self = [super init]) {
41         _userId = [aDecoder decodeObjectForKey:@"userId"];        // 用户ID
42         _userName = [aDecoder decodeObjectForKey:@"userName"];    // 用户名
43         _password = [aDecoder decodeObjectForKey:@"password"];    // 用户密码
44         _age = [aDecoder decodeIntegerForKey:@"age"];             // 用户的年龄
45         _weight = [aDecoder decodeDoubleForKey:@"weight"];        // 用户的体重
46         _height = [aDecoder decodeFloatForKey:@"height"];         // 用户的身高
47         _userTel = [aDecoder decodeObjectForKey:@"userTel"];      // 用户的电话号码
48         _userEmail = [aDecoder decodeObjectForKey:@"userEmail"];  // 用户的邮箱
49     }
50     return self;
51 }
52 
53 @end

  2)保存数据、读取数据工具类:UserDataSaveTool类

1 // UserDataSaveTool.h文件
 2 #import <Foundation/Foundation.h>
 3 #import "UserModel.h"
 4 
 5 @interface UserDataSaveTool : NSObject
 6 
 7 #pragma mark - 保存用户数据
 8 + (void)saveUserModel:(UserModel *)userModel;
 9 
10 #pragma mark - 获取用户数据
11 + (UserModel *)getUserModel;
12 
13 @end
14 
15 
16 // UserDataSaveTool.m文件
17 #import "UserDataSaveTool.h"
18 
19 @implementation UserDataSaveTool
20 
21 #pragma mark - 用户信息保存的沙盒路径
22 + (NSString *)getAccoutPath
23 {
24     // 归档文件名为:account.archive
25     NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"account.archive"];
26     return filePath;
27 }
28 
29 #pragma mark - 保存用户数据
30 + (void)saveUserModel:(UserModel *)userModel
31 {
32     // 用户信息(用户信息model:accout)归档 -- 执行这个就会自动跳转执行用户model里的归档协议
33     [NSKeyedArchiver archiveRootObject:userModel toFile:[self getAccoutPath]];
34 }
35 
36 #pragma mark - 获取用户数据
37 + (UserModel *)getUserModel
38 {
39     // 取出账号(账号存的就是一个model) -- 执行这个就会自动跳转执行用户model里的解档协议
40     UserModel *userModel = [NSKeyedUnarchiver unarchiveObjectWithFile:[self getAccoutPath]];
41     return userModel;
42 }
43 
44 @end

  3)实际调用

1 #pragma mark - 数据保存
 2 - (void)saveData
 3 {
 4     // 1.初始化自定义对象模型
 5     UserModel *userModel = [[UserModel alloc] init];
 6     userModel.userId = @"20099830215";
 7     userModel.userName = @"Frank";
 8     userModel.password = @"9830215";
 9     userModel.age = 25;
10     userModel.weight = 110.0;
11     userModel.height = 1.69;
12     userModel.userTel = @"15112256959";
13     userModel.userEmail = @"1058842360@qq.com";
14     
15     // 2.借助UserDataSaveTool工具保存对象模型
16     [UserDataSaveTool saveUserModel:userModel];
17 }
18 
19 #pragma mark - 读取数据
20 - (void)readData
21 {
22     // 1.借助UserDataSaveTool工具获取对象模型
23     UserModel *userModel = [UserDataSaveTool getUserModel];
24     
25     // 2.打印输出
26     NSLog(@"userModel = %@", userModel);
27 }

3.注意

  1)对象必须遵循NSCoding协议,并且重写协议的两个方法

  2)如果是继承,在子类中也是要重写NSCoding协议的两个方法,而且协议方法中必须先调用父类的方法

  例如:给上面的UserModel添加一个属性"sex"

1 // PersonModel.h文件
 2 #import "UserModel.h"
 3 
 4 @interface PersonModel : UserModel
 5 
 6 #pragma mark - 给子类的属性
 7 @property (nonatomic, copy) NSString *sex;  // 性别
 8 
 9 @end
10 
11 
12 // PersonModel.m文件
13 #import "PersonModel.h"
14 
15 @implementation PersonModel
16 
17 #pragma mark - 重写协议方法<NSCoding>
18 // 1.归档
19 - (void)encodeWithCoder:(NSCoder *)aCoder
20 {
21     // 先调用父类的归档协议方法
22     [super encodeWithCoder:aCoder];
23     [aCoder encodeObject:@"M" forKey:@"sex"];
24 }
25 // 2.解档
26 - (instancetype)initWithCoder:(NSCoder *)aDecoder
27 {
28     // 先初始化父类
29     if (self = [super initWithCoder:aDecoder]) {
30         [aDecoder decodeObjectForKey:@"sex"];
31     }
32     return self;
33 }
34 
35 @end

 

六、SQLite3

1.前言

  在我们的实际开发中都需要做一些离线数据的处理,即离线缓存。上面三个方法都可以实现离线缓存,但是它们都有一个致命的弱点:无法存储大批量的数据,存在一些性能的问题。除了这个关键性的问题外,它们还存在各种各样不同的约束问题。例如:对于归档,它只支持一次性写入,一次性读取,这对于大数据的存储有着很大的问题。针对这些问题,数据库SQLite3可以解决。

2.SQLite3的简介

  1)3是SQLite的版本号

  2)SQLite3是一种轻量级的嵌入式数据库,目前安卓和iOS使用的都是SQLite数据库

  3)SQLite的占用资源非常低、处理速度也较MySql、PostgreSQL这两款著名的数据库快

  4)SQLite中可以有多张表,表里的元素都是以字段存储和读取

3.SQL语句

  1)SQL语句中字段的类型

    integer:整型

    real:浮点类型

    text:文本字符串

    blob:二进制数据(比如文件)

  2)SQL语句的基本的操作

    a.创表

      create table 表名(字段名1 字段类型, 字段名2 字段类型, 字段名3 字段类型, ...);

      create table if not exists 表名(字段名1 字段类型, 字段名2 字段类型, 字段名3 字段类型, ...);

      // 实际上SQLite是无类型的,就算声明为integer类型,还是能存储字符串文本(主键除外),创表时可以不用去声明字段类型

    b.删表

      drop table 表明;

      drop table if exists 表名;

    c.插入数据

      insert into 表名(字段1, 字段2, ...) values (字段1的值, 字段2的值, ...);

      例:insert into t_student(name, age, sex) values ('Frank', 25, 'M');

      // 注意:SQL中字符串的值必须使用单引号''引上

    d.更新数据

      update 表名 set 字段1 = 字段1的值, 字段2 = 字段2的值, ...;

      例:update t_student set name = 'Frank', age = 25;

      // 上面语句是将表t_student中所有记录的字段name的值改为"Frank",所有记录的字段age的值改为25

    e.删除数据

      delete from 表名;

      例:delete from t_student;

      // 上面语句是将表t_student中的所有记录删除

    d.条件语句

      where 指定字段 = 指定值;

      where 指定字段 is 指定值;

      where 指定字段 != 指定值;

      where 指定字段 is not 指定值;

      where 指定字段 > 指定值;

      where 指定字段1 = 指定值1 and 指定字段2 > 指定值2;

      where 指定字段1 = 指定值1 or 指定字段2 = 指定值2;

    e.查找语句

      select 字段1, 字段2, ... from 表名;        // 查询指定字段

      select * from 表名;                             // 查询所有字段

    f.排序

      select * from 表名 order by 字段;      // 查询结果按照指定字段升序排列(默认)

      select * from 表名 order by 字段 desc;      // 查询结果按照指定字段降序排列

      select * from 表名 order by 字段 asc;      // 查询结果按照指定字段升序排列 

      select * from 表名 order by 字段1 desc, 字段2 asc;  // 查询结果按照字段1降序排列,按照字段2升序排列

    g.使用limit精准控制查询结果的数量

      select * from 表名 limit 数值;                  // 指定获取查询结果的前几条数据

      select * from 表名 limit 数值1, 数值2;       // 指定跳过前面几条(数值1)数据,然后取指定条(数值2)数据

    h.普通字段约束

      创表时,可以给一些特定的字段设置一些约束条件,常见的有:

        not null:规定字段值不能为null

        unique:规定字段的值必须唯一

        default:指定字段的默认值

      例:create table t_student (id integer, name text not null unique, age integer not null default 1);

        // name字段:不能为null,并且唯一

        // age字段:不能为null,默认值为1

    i.主键约束

      主键用来唯一标识某一条记录。主键可以是一个字段或多个字段

      只要声明为primary key ,就说明是一个主键字段

      主键字段默认就包含了not null和unique两个约束

      主键自动增长,应该增加autoincrement

      例:create table t_student (id integer primary key autoincrement, name text, age integer); 

        // 自动增长的主键

    j.外键约束

      外键约束可以用来建立表与表之间的联系

      例:create table t_student (id integer primary key autoincrement, name text, class_id integer, constraint fk_student_class foreign key (class_id) references t_class (id));

        // t_student表中的外键:fk_t_student_class_id_t_class_id

        // 作用:用t_student表中的class_id字段引用t_class表的id字段

4.FMDB      

  1)什么是FMDB

    a.FMDB是iOS平台的SQLite数据库框架

    b.FMDB以OC的方式封装了SQLite的C语言API

  2)FMDB的优点

    a.使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码

    b.对比苹果自带的CoreData框架,更加轻量级和灵活

    c.提供了多线程安全的数据库操作方法,有效地防止数据混乱

  3)FMDB的核心类

    a.FMDatabase:一个FMDatabase对象就代表一个单独的SQLite数据库。用来执行SQL语句

    b.FMResultSet:使用FMDatabase执行查询后的结果集

    c.FMDdatabaseQueue:用于多线程中执行多个查询或更新,它是线程安全的

  4)打开数据库(根据文件路径)

    FMDatabase *db = [FMDatabase databaseWithPath:dbPath];

    if (![db open]) {

      NSLog(@"数据库打开失败!");

    }

    // 文件路径有三种情况:

    a.文件路径具体:如果不存在该数据库文件会自动创建

    b.文件路径为空字符串@"":会在临时目录下创建一个空的数据库;当FMDatabase连接关闭时,数据库文件也被删除

    c.文件路径为nil:会创建一个内存中临时数据库;当FMDatabase连接关闭时,数据库会被销毁

  5)FMDB的用法

    a.在项目中导入系统框架libsqlite3.dylib

    b.将第三方库FMDB拖入到项目中

  6)FMDB管理工具类

1 // FMDBManager.h文件
  2 #import <Foundation/Foundation.h>
  3 #import "FMDatabaseQueue.h"
  4 #import "FMDatabaseAdditions.h"
  5 #import <objc/runtime.h>
  6 
  7 @interface FMDBManager : NSObject
  8 
  9 
 10 // 工具类特点:
 11 // 1.根据实体对象创表,每张表的主键为实体类的首个属性名,主键为自增NSInteger类型
 12 // 2.删除和插入可以灵活地传入实体对应的任何一个属性作为依据字段,根据该字段来判断删除的记录是否存在和判断是插入还是更新操作
 13 // 3.查找方法可以将一个字符串作为参数传递,该字符串为条件查找的条件语句;方法内部会根据该字符串是否为nil,来判断是条件查询还是全部查询
 14 
 15 // 工具类的要求:
 16 // 1.实体对应的首字段取名最好为“idNum”
 17 // 2.实体所有属性最好都是NSString类型
 18 
 19 #pragma mark - 数据库管理类对象
 20 + (FMDBManager *)sharedInstace;
 21 
 22 #pragma mark - 创建表
 23 - (void)creatTable:(id)model;
 24 
 25 #pragma mark - 数据库更新或插入数据
 26 - (void)insertAndUpdateModelToDatabase:(id)model selectColumn:(NSString *)selectColumn;
 27 
 28 #pragma mark - 删除元素
 29 - (void)deleteModelInDatabase:(id)model selectColumn:(NSString *)selectColumn;
 30 
 31 #pragma mark - 全部查找
 32 - (NSArray *)selectModelArrayInDatabase:(NSString *)className factorString:(NSString *)factorString;
 33 
 34 @end
 35 
 36 
 37 // FMDBManager.m文件
 38 #import "FMDBManager.h"
 39 
 40 // 通过实体获取类名
 41 #define KCLASS_NAME(model) NSStringFromClass([model class])
 42 // 通过实体获取属性数组
 43 #define KMODEL_PROPERTYS [self getAllProperties:model]
 44 // 通过实体获取属性数组的数目
 45 #define KMODEL_PROPERTYS_COUNT [[self getAllProperties:model] count]
 46 
 47 @implementation FMDBManager
 48 {
 49     FMDatabaseQueue *_dbQueue; // 数据库操作队列
 50 }
 51 
 52 #pragma mark - 数据库管理类对象
 53 + (FMDBManager *)sharedInstace
 54 {
 55     static dispatch_once_t onceToken;
 56     static FMDBManager *manager = nil;
 57     dispatch_once(&onceToken, ^{
 58         manager = [[FMDBManager alloc] init];
 59     });
 60     return manager;
 61 }
 62 
 63 #pragma mark - 获取沙盒路径
 64 - (NSString *)getDBFilePath
 65 {
 66     NSString *dbFilePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"db.sqlite"];
 67     NSLog(@"%@", dbFilePath);
 68     return dbFilePath;
 69 }
 70 
 71 #pragma mark - 创建数据库操作队列
 72 // 一个数据库文件对应一个数据库操作队列
 73 - (void)creatDBQueue
 74 {
 75     // 创建数据库操作队列的同时还会判断数据库是否存在,如果不存在,系统自动创建数据库
 76     _dbQueue = [[FMDatabaseQueue alloc] initWithPath:[self getDBFilePath]];
 77 }
 78 
 79 #pragma mark - 获取实体对象的属性数组
 80 // 参数:model 实体对象
 81 // 返回值:实体对象属性数组
 82 - (NSArray *)getAllProperties:(id)model
 83 {
 84     u_int count;
 85     objc_property_t *properties  = class_copyPropertyList([model class], &count);
 86     // 定义一个可变的属性数组
 87     NSMutableArray *propertiesArray = [NSMutableArray array];
 88     for (int i = 0; i < count ; i++)
 89     {
 90         const char* propertyName = property_getName(properties[i]);
 91         [propertiesArray addObject: [NSString stringWithUTF8String: propertyName]];
 92     }
 93     free(properties);
 94     return propertiesArray;
 95 }
 96 
 97 #pragma mark - 创表
 98 // 参数1:model 实体对象
 99 - (void)creatTable:(id)model
100 {
101     // 判断数据库操作队列是否存在
102     if (!_dbQueue) {
103         // 数据库操作队列不存,就得创建
104         [self creatDBQueue];
105     }
106     
107     // 使用数据库操作队列操作数据库
108     [_dbQueue inDatabase:^(FMDatabase *db) {
109         // 打开数据库
110         if (![db open]) {
111             // 数据库打开失败
112             NSLog(@"数据库打开失败");
113             return;
114         }
115         
116         // 为数据设置缓存,提高查询效率
117         [db setShouldCacheStatements:YES];
118         
119         // 判断数据库中是否已经存在该实体对应的表
120         // 不存在该实体对应的表,才创建表
121         if (![db tableExists:KCLASS_NAME(model)]) {
122             // 创表语句的第一部分
123             // %@ integer primary key autoincrement(设置主键为实体model的首字段,integer类型,自增)
124             // 主键
125             NSString *primaryKey = KMODEL_PROPERTYS[0];
126             NSString *creatTableOneStr = [NSString stringWithFormat:@"create table %@(%@ integer primary key autoincrement", KCLASS_NAME(model), primaryKey];
127             
128             // 创表语句的第二部分
129             NSMutableString *createTableTwoStr = [NSMutableString string];
130             // 遍历实体对象的属性数组
131             // 直接跳过实体的首字段
132             for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) {
133                 // 实体对应的字段都是以文本字符串类型存储到数据库表中
134                 NSString *popertyName = KMODEL_PROPERTYS[i];
135                 [createTableTwoStr appendFormat:@", %@ text", popertyName];
136             }
137             
138             // 创建表总语句
139             NSString *createTableStr = [NSString stringWithFormat:@"%@%@)", creatTableOneStr, createTableTwoStr];
140             
141             // 创表方法
142             if ([db executeUpdate:createTableStr]) {
143                 NSLog(@"创表成功");
144             }
145         }
146         
147         // 关闭数据库
148         [db close];
149     }];
150 }
151 
152 #pragma mark - 数据库更新或插入数据
153 // 参数1:model 实体对象
154 // 参数2:selectColumn 依据字段
155 - (void)insertAndUpdateModelToDatabase:(id)model selectColumn:(NSString *)selectColumn
156 {
157     // 判断数据库操作队列是否存在
158     if (!_dbQueue) {
159         // 数据库操作队列不存,就得创建
160         [self creatDBQueue];
161     }
162     
163     // 使用数据库操作队列操作数据库
164     [_dbQueue inDatabase:^(FMDatabase *db) {
165         // 打开数据库
166         if (![db open]) {
167             // 数据库打开失败
168             NSLog(@"数据库打开失败");
169             return;
170         }
171         
172         // 为数据设置缓存,提高查询效率
173         [db setShouldCacheStatements:YES];
174         
175         // 判断是否存在该实体对象所对应的表
176         if (![db tableExists:KCLASS_NAME(model)]) {
177             // 不存在该实体对象所对应的表就去创表
178             [self creatTable:model];
179         }
180         
181         // 按照传进来的查找字段查找该条记录是否存在
182         NSString *selectStr = [NSString stringWithFormat:@"select * from %@ where %@ = ?", KCLASS_NAME(model), selectColumn];
183         // 获取实体对象的首个属性对应的值
184         NSString *selectColumnValue = [model valueForKey:selectColumn];
185         // 查找
186         FMResultSet *resultSet = [db executeQuery:selectStr, selectColumnValue];
187         // 判断查找结果是否为空
188         // 如果有查找结果,对应实体对象就是更新操作
189         if ([resultSet next]) {
190             // 更新语句第一部分
191             NSString *updateOneStr = [NSString stringWithFormat:@"update %@ set ",KCLASS_NAME(model)];
192             
193             // 更新语句第二部分
194             NSMutableString *updateTwoStr = [NSMutableString string];
195             // 遍历实体对象的属性数组
196             // 跳过实体对应的首字段
197             for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) {
198                 // 获取实体属性字段
199                 NSString *propertyName = KMODEL_PROPERTYS[i];
200                 if ([propertyName isEqualToString:selectColumn]) {
201                     continue;
202                 }
203                 [updateTwoStr appendFormat:@"%@ = ?", propertyName];
204                 // 判断是否遍历到属性数组的最后一个
205                 if (i != KMODEL_PROPERTYS_COUNT - 1) {
206                     [updateTwoStr appendFormat:@", "];
207                 }
208             }
209             
210             // 更新语句第三部分
211             NSString *updateThreeStr = [NSString stringWithFormat:@" where %@ = %@",selectColumn, selectColumnValue];
212             
213             // 更新总语句
214             NSString *updateStr = [NSString stringWithFormat:@"%@%@%@", updateOneStr, updateTwoStr, updateThreeStr];
215             
216             // 属性值数组
217             NSMutableArray *propertyValueArr = [NSMutableArray array];
218             // 遍历属性数组
219             // 跳过实体对应的首字段
220             for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) {
221                 NSString *propertyName = KMODEL_PROPERTYS[i];
222                 if ([propertyName isEqualToString:selectColumn]) {
223                     continue;
224                 }
225                 NSString *propertyValue = [model valueForKey:propertyName];
226                 if (propertyValue == nil) {
227                     propertyValue = @"";
228                 }
229                 [propertyValueArr addObject:propertyValue];
230             }
231             
232             // 调用更新方法
233             if([db executeUpdate:updateStr withArgumentsInArray:propertyValueArr])
234             {
235                 NSLog(@"数据更新成功");
236             }
237         } else {
238             // 插入操作
239             
240             // 插入语句第一部分
241             NSString *insertOneStr = [NSString stringWithFormat:@"insert into %@ (",KCLASS_NAME(model)];
242             
243             // 插入语句第二部分
244             NSMutableString *insertTwoStr =[NSMutableString string];
245             // 跳过实体对应的首字段
246             for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) {
247                 // 获取实体属性字段
248                 NSString *propertyName = KMODEL_PROPERTYS[i];
249                 [insertTwoStr appendFormat:@"%@", propertyName];
250                 if (i != KMODEL_PROPERTYS_COUNT - 1) {
251                     [insertTwoStr appendFormat:@", "];
252                 }
253             }
254             
255             // 插入语句第三部分
256             NSString *insertThreeStr =[NSString stringWithFormat:@") values ("];
257             
258             // 插入语句第四部分
259             NSMutableString *insertFourStr =[NSMutableString string];
260             // 跳过实体对应的首字段
261             for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) {
262                 [insertFourStr appendFormat:@"?"];
263                 if (i != KMODEL_PROPERTYS_COUNT - 1) {
264                     [insertFourStr appendFormat:@", "];
265                 }
266             }
267             
268             // 插入整个语句
269             NSString *insertStr = [NSString stringWithFormat:@"%@%@%@%@)", insertOneStr, insertTwoStr, insertThreeStr, insertFourStr];
270             
271             // 属性值数组
272             NSMutableArray *propertyValueArr = [NSMutableArray array];
273             // 遍历属性数组
274             // 跳过实体对应的首字段
275             for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) {
276                 // 获取实体属性字段
277                 NSString *propertyName = KMODEL_PROPERTYS[i];
278                 // 通过实体的字段获取对应的值
279                 NSString *propertyValue = [model valueForKey:propertyName];
280                 // 判断这个值是否为nil
281                 if (propertyValue == nil) {
282                     propertyValue = @"";
283                 }
284                 [propertyValueArr addObject:propertyValue];
285             }
286             
287             // 调用插入方法
288             if([db executeUpdate:insertStr withArgumentsInArray:propertyValueArr])
289             {
290                 NSLog(@"插入成功");
291             }
292             
293             // 关闭数据库
294             [db close];
295         }
296     }];
297 }
298 
299 #pragma mark - 删除元素
300 // 参数1:model 实体对象
301 // 参数2:selectColumn 依据字段
302 - (void)deleteModelInDatabase:(id)model selectColumn:(NSString *)selectColumn
303 {
304     // 判断数据库操作队列是否存在
305     if (!_dbQueue) {
306         // 数据库操作队列不存,就得创建
307         [self creatDBQueue];
308     }
309     
310     // 使用数据库操作队列操作数据库
311     [_dbQueue inDatabase:^(FMDatabase *db) {
312         // 打开数据库
313         if (![db open]) {
314             // 数据库打开失败
315             NSLog(@"数据库打开失败");
316             return;
317         }
318         
319         // 为数据设置缓存,提高查询效率
320         [db setShouldCacheStatements:YES];
321         
322         // 判断是否存在该实体对象所对应的表
323         if (![db tableExists:KCLASS_NAME(model)]) {
324             // 不存在该实体对象所对应的表就去创表
325             [self creatTable:model];
326         }
327         
328         // 删除语句(根据实体首字段的首属性字段去删)
329         NSString *deleteStr = [NSString stringWithFormat:@"delete from %@ where %@ = ?", KCLASS_NAME(model), selectColumn];
330         
331         // 执行删除方法
332         // 实体字段对应的值
333         NSString *selectColumnValue = [model valueForKey:selectColumn];
334         if([db executeUpdate:deleteStr, selectColumnValue])
335         {
336             NSLog(@"删除成功");
337         }
338         
339         // 关闭数据库
340         [db close];
341         
342     }];
343 }
344 
345 #pragma mark - 查找
346 // 参数1:tableName 表名
347 // 参数2:factorString 条件查找语句
348 // 返回值:直接返回实体数组
349 - (NSArray *)selectModelArrayInDatabase:(NSString *)tableName factorString:(NSString *)factorString
350 {
351     // 判断数据库操作队列是否存在
352     if (!_dbQueue) {
353         // 数据库操作队列不存,就得创建
354         [self creatDBQueue];
355     }
356     
357     // 需要返回的实体数组(block里面需要改变其值,所以必须使用__block修饰)
358     __block NSMutableArray *modelArray = [NSMutableArray array];
359     
360     // 使用数据库操作队列操作数据库
361     [_dbQueue inDatabase:^(FMDatabase *db) {
362         // 打开数据库
363         if (![db open]) {
364             // 数据库打开失败
365             NSLog(@"数据库打开失败");
366             return;
367         }
368         
369         // 为数据设置缓存,提高查询效率
370         [db setShouldCacheStatements:YES];
371         
372         // 条件查询
373         NSString * selectStr = nil;
374         if (factorString != nil) {
375             selectStr = [NSString stringWithFormat:@"SELECT * FROM %@ %@",tableName, factorString];
376         }else {
377             // 全局查询
378             selectStr = [NSString stringWithFormat:@"select * from %@",tableName];
379         }
380         
381         FMResultSet *resultSet = [db executeQuery:selectStr];
382         while([resultSet next]) {
383             // 使用表名作为类名创建对应的类的对象
384             id model = [[NSClassFromString(tableName) alloc] init];
385             for (int i = 0; i < KMODEL_PROPERTYS_COUNT; i++) {
386                 // 值是从我们的数据表的Column字段取出来
387                 // 获取表中字段
388                 NSString *propertyName = KMODEL_PROPERTYS[i];
389                 NSString *propertyValue = [resultSet stringForColumn:propertyName];
390                 // 设置model对应属性的对应值
391                 [model setValue:propertyValue forKey:propertyName];
392             }
393             [modelArray addObject:model];
394         }
395     }];
396     return modelArray;
397 }
398 
399 @end

  7)事务

    例:银行转账系统,张三和李四账户上都有1000元钱,张三现在给李四转账500元

      update t_account set money = 500 where name = @"张三";

      update t_account set money = 1500 where name = @"李四";

      张三给李四转账500元,这一操作就会执行上面两条SQL语句,但是考虑到安全性,我们必须要求两条语句同生同死。这个时候我们可以将他放在同一个事务中

    事务:把多条语句放到同一个事务中,就会同生共死(即使前面的SQL语句执行成功,而后面的SQL执行失败,系统也会回滚,使它们全部失败)

1     [_dbQueue inDatabase:^(FMDatabase *db) {
2         // 创建事务
3         [db beginTransaction];
4         [db executeUpdate:@"insert into t_person (name, age) values (?, ?);", @"Frank", @22];
5         [db executeUpdate:@"insert into t_person (name, age) values (?, ?);", @"Kin", @15];
6         // 提交事务
7         [db commit];
8         }];