最全iOS数据存储方法介绍:FMDB,SQLite3 ,Core Data,Plist,Preference偏好设置,NSKeyedArchiver归档

品类准备利用的Core Data举办本地数据存储,本来打算只写一下Core
Data的,但是既然说到了多少存储,干脆来个数据存储基础大总计!本文将对以下几个模块举办描述。

  1. 沙盒
  2. Plist
  3. Preference偏好设置
  4. NSKeyedArchiver归档 / NSKeyedUnarchiver解档
  5. SQLite3的使用
  6. FMDB
  7. Core Data

下图是Core Data堆栈的图示,在此处是为着做著作的书皮图片,后文会介绍Core
Data的接纳形式。

Core Data

一、沙盒

iOS本地化存储的数额保存在沙盒中,
并且每个应用的沙盒是相持独立的。每个应用的沙盒文件结构都是一律的,如下图所示:

沙盒目录

Documents:iTunes会备份该目录。一般用来存储需要持久化的数码。

Library/Caches:缓存,iTunes不会备份该目录。内存不足时会被拔除,应用尚未运行时,可能会被排除,。一般存储体积大、不需要备份的非首要数据。

Library/Preference:iTunes同会备份该目录,可以用来囤积一些偏好设置。

tmp
iTunes不会备份这么些目录,用来保存临时数据,应用退出时会清除该目录下的数量。

何以得到各种文件夹的路子呢?我这边就只说最好的一种模式。

// 这个方法返回的是一个数组,iOS中这个数组其实只有一个元素,所以我们可以用lastObject或lastObject来拿到Documents目录的路径
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
// 得到Document目录下的test.plist文件的路径
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test.plist"];

地方的办法有三个参数,这里分别说一下。

NSDocumentDirectory:
首个参数代表要物色哪个文件,是一个枚举,点进入看一下发觉有诸多选项,为了直接找到沙盒中的Documents目录,我们一般用NSDocumentDirectory。

NSUserDomainMask:
也是一个枚举,表示搜索的限量界定于当下应用的沙盒目录。我们一般就挑选NSUserDomainMask。

YES (expandTilde):
第六个参数是一个BOOL值。iOS中主目录的全写情势是/User/userName,这么些参数填YES就象征全写,填NO就是写成‘‘~’’,我们一般填YES。

依据地点的文字,你利用可以精通哪些得到Library/Caches目录下的文本路径了啊?没错,就是这般:

//获取Library/Caches目录路径
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; 
//获取Library/Caches目录下的test.data文件路径
NSString *filePath = [path stringByAppendingPathComponent:@"test.data"];

//获取temp路径
NSString *tmp= NSTemporaryDirectory();
//获取temp下test.data文件的路径
NSString *filePath = [tmp stringByAppendingPathComponent:@"test.data"];

据此,借使程序中有需要长日子持久化的多少,就分选Documents,尽管有体积大不过并不重要的数据,就可以挑选交给Library,而暂时没用的数量当然是停放temp。至于Preference则可以用来保存一些设置类信息,后边会讲到偏好设置的应用方法。

推介一个好用的分类工具集合WHKit,可以一向利用分类方法拿到文件路径。

//一行代码搞定
NSString *filePath = [@"test.plist" appendDocumentPath];

//使用WHKit,上面这一行代码等同于下面这两行。

NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test.plist"];

二、Plist存储

Plist文件的Type可以是字典NSDictionary或数组NSArray,也就是说可以把字典或数组直接写入到文件中。
NSString、NSData、NSNumber等门类,也足以运用writeToFile:atomically:方法直接将目的写入文件中,只是Type为空。

上面就举个例子来看一下什么行使Plist来囤积数据。

//准备要保存的数据
NSDictionary *dict = [NSDictionary dictionaryWithObject:@"first" forKey:@"1"];

//获取路径,你也可以利用上面说WHKit更优雅的拿到路径
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test.plist"];

//写入数据
[dict writeToFile:filePath atomically:YES];

下面的代码运行之后,在采取沙盒的Documents中就创建了一个plist文件,并且已经写入数据保存。

写入plist

多少存储了,那么哪些读取呢?

//拿到plist文件路径,你也可以利用上面说WHKit更优雅的拿到路径
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test.plist"];

//解析数据,log出的结果为first,读取成功
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSString *result = dict[@"1"];
NSLog(@"%@",result);

下面这段代码就读出了plist种的多寡。

三、Preference偏好设置

偏好设置的施用相当方便赶快,我们一般采用它来进展局部装置的笔录,比如用户名,开关是否打开等装置。
Preference是通过NSUserDefaults来利用的,是经过键值对的方法记录设置。下边举个例子。

运用NSUserDefaults判断APP是不是第一次开行。

// 启动的时候判断key有没有value,如果有,说明已经启动过了,如果没有,说明是第一次启动
if (![[NSUserDefaults standardUserDefaults] valueForKey:@"first"]) {

    //如果是第一次启动,就运用偏好设置给key设置一个value
    [[NSUserDefaults standardUserDefaults] setValue:@"start" forKey:@"first"];
    NSLog(@"是第一次启动");

} else {
    NSLog(@"不是第一次启动");
}

透过键值对的法门非凡easy的保存了数量。

注意:NSUserDefaults可以储存的数据类型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。如若要存储其他体系,则需要转移为眼前的体系,才能用NSUserDefaults存储。

上面的事例是用NSUserDefaults存储图片,需要先把图片转换成NSData类型。

UIImage *image=[UIImage imageNamed:@"photo"];
//UIImage对象转换成NSData
NSData *imageData = UIImageJPEGRepresentation(image, 100);

//偏好设置可以保存NSData,但是不能保存UIimage
[[NSUserDefaults standardUserDefaults] setObject:imageData forKey:@"image"];

// 读出data
NSData *getImageData = [[NSUserDefaults standardUserDefaults] dataForKey:@"image"];
//NSData转换为UIImage
UIImage *Image = [UIImage imageWithData:imageData];

NSUserDefaults是不是很好用!但是有没有觉得每便都写[NSUserDefaults
standardUserDefaults]有点烦,那么又到了推介环节,好用的分类工具WHKit,这个工具中有过多好用的宏定义,其中一个就是KUSERDEFAULT,等同于[NSUserDefaults
standardUserDefaults]。
WHKit中还有更多好用的宏定义等着你!

四、NSKeyedArchiver归档 / NSKeyedUnarchiver解档

归档和解档会在写入、读出多少在此之前开展系列化、反系列化,数据的安全性相对高一些。
NSKeyedArchiver可以有五个应用情状。

1.对单个简单对象开展归档/解档

与plist差不多,对于简易的数额举行归档,直接写入文件路径。

//获取归档文件路径
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
NSString *filePath = [documentPath stringByAppendingPathComponent:@"test"];

//对字符串@”test”进行归档,写入到filePath中
[NSKeyedArchiver archiveRootObject:@"test" toFile:filePath];

//根据保存数据的路径filePath解档数据
NSString *result = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
//log结构为@”test”,就是上面归档的数据
NSLog(@"%@",result);

当然,也得以储存NSArray,NSDictionary等目标。

2.对四个对象开展归档/解档

这种意况可以一遍保存多种不同类型的多少,最后利用的是与plist相同的writeToFile:(NSString
*)path atomically:(BOOL)useAuxiliaryFile来写入数据。

    //获取归档路径
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    NSString *filePath = [documentPath stringByAppendingPathComponent:@"test"];

    //用来承载数据的NSMutableData
    NSMutableData *data = [[NSMutableData alloc] init];
    //归档对象
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

    //将要被保存的三个数据
    NSString *name = @"jack";
    int age = 17;
    double height = 1.78;

    //运用encodeObject:方法归档数据
    [archiver encodeObject:name forKey:@"name"];
    [archiver encodeInt:age forKey:@"age"];
    [archiver encodeDouble:height forKey:@"height"];
    //结束归档
    [archiver finishEncoding];
    //写入数据(存储数据)
    [data writeToFile:filePath atomically:YES];


    //NSMutableData用来承载解档出来的数据
    NSMutableData *resultData = [[NSMutableData alloc] initWithContentsOfFile:filePath];
    //解档对象
    NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:resultData];

    //分别解档出三个数据
    NSString *resultName = [unArchiver decodeObjectForKey:@"name"];
    int resultAge = [unArchiver decodeIntForKey:@"age"];
    double resultHeight = [unArchiver decodeDoubleForKey:@"height"];
    //结束解档
    [unArchiver finishDecoding];
    //成功打印出结果,说明成功归档解档
    NSLog(@"name = %@, age = %d, height = %.2f",resultName,resultAge,resultHeight);

3.归档保存自定义对象

本身觉着这是归档最常使用的现象。
概念一个Person类,假设想对person进行归档解档,首先要让Person遵从<NSCoding>协议。

/********Person.h*********/

#import <Foundation/Foundation.h>

//遵守NSCoding协议
@interface Person : NSObject<NSCoding>

@property (nonatomic, copy) NSString *name;

@property (nonatomic, assign) NSInteger age;

//自定义的归档保存数据的方法
+(void)savePerson:(Person *)person;

//自定义的读取沙盒中解档出的数据
+(Person *)getPerson;

@end

NSCoding协议有2个方法:

  • (void)encodeWithCoder:(NSCoder *)aCoder
    归档时调用这一个措施,在措施中行使encodeObject:forKey:归档变量。
  • (instancetype)initWithCoder:(NSCoder *)aDecoder
    解档时调用那么些模式,在点子中石油decodeObject:forKey读出变量。
/******Person.m*******/

#import "Person.h"

@implementation Person

//归档,Key建议使用宏代替,这里就不使用了
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}

//解档
-(instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self=[super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.age = [aDecoder decodeIntegerForKey:@"age"];
    }
    return self;
}

//类方法,运用NSKeyedArchiver归档数据
+(void)savePerson:(Person *)person {
    NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    NSString *path=[docPath stringByAppendingPathComponent:@"Person.plist"];
    [NSKeyedArchiver archiveRootObject:person toFile:path];
}

//类方法,使用NSKeyedUnarchiver解档数据
+(Person *)getPerson {
    NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    NSString *path=[docPath stringByAppendingPathComponent:@"Person.plist"];
    Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    return person;
}

@end

下面就可以在需要的地方归档或解档Person对象。

/*******ViewController.m*******/

    //创建Person对象
    Person *person = [Person new];
    person.name = @"jack";
    person.age = 17;
    //归档保存数据
    [Person savePerson:person];
    //解档拿到数据
    Person *resultPerson = [Person getPerson];
    //打印出结果,证明归档解档成功
    NSLog(@"name = %@, age = %ld",resultPerson.name,resultPerson.age);

五、SQLite3的使用

1、首先需要添加库文件libsqlite3.0.tbd
2、导入头文件#import <sqlite3.h>
3、打开数据库
4、创建表
5、对数据表举办增删改查操作
6、关闭数据库

上代码在此以前,有些问题你需要了然。

  • SQLite 不区分轻重缓急写,但也有需要小心的地点,例如GLOB 和 glob
    具有不同功能。

  • SQLite3有5种为主数据类型 text、integer、float、boolean、blob

  • SQLite3是无类型的,在创造的时候你可以不申明字段的类型,可是仍旧指出加上数据类型

 create table t_student(name, age);

 create table t_student(name text, age integer);

下面的代码就是SQLite3的骨干使用方法,带有详细申明。
代码中用到了一个Student类,这几个类有多少个属性name和age。

/****************sqliteTest.m****************/

#import "sqliteTest.h"
//1.首先导入头文件
#import <sqlite3.h>

//2.数据库
static sqlite3 *db;

@implementation sqliteTest

/** 3.打开数据库 */
+ (void)openSqlite {

    //数据库已经打开
    if (db != nil) {
        NSLog(@"数据库已经打开");
        return;
    }

    //创建数据文件路径
    NSString *string = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString *path = [string stringByAppendingPathComponent:@"Student.sqlite"];
    NSLog(@"%@",path);

    //打开数据库
    int result = sqlite3_open(path.UTF8String, &db);

    if (result == SQLITE_OK) {
        NSLog(@"数据库打开成功");
    } else {
        NSLog(@"数据库打开失败");
    }
}

/** 4.创建表 */
+ (void)createTable {

    //创建表的SQLite语句,其中id是主键,not null 表示在表中创建纪录时这些字段不能为NULL
    NSString *sqlite = [NSString stringWithFormat:@"create table if not exists t_student (id integer primary key autoincrement, name text not null, age integer)"];

    //用来记录错误信息
    char *error = NULL;

    //执行SQLite语句
    int result = sqlite3_exec(db, sqlite.UTF8String, nil, nil, &error);

    if (result == SQLITE_OK) {
        NSLog(@"创建表成功");
    }else {
        NSLog(@"创建表失败");
    }
}

/** 5.添加数据 */
+ (void)addStudent:(Student *)stu {

    //增添数据的SQLite语句
    NSString *sqlite = [NSString stringWithFormat:@"insert into t_student (name,age) values ('%@','%ld')",stu.name,stu.age];
    char *error = NULL;

    int result = sqlite3_exec(db, [sqlite UTF8String], nil, nil, &error);

    if (result == SQLITE_OK) {
        NSLog(@"添加数据成功");
    } else {
        NSLog(@"添加数据失败");
    }
}

/** 6.删除数据 */
+ (void)deleteStuWithName:(NSString *)name {

    //删除特定数据的SQLite语句
    NSString *sqlite = [NSString stringWithFormat:@"delete from t_student where name = '%@'",name];
    char *error = NULL;

    int result = sqlite3_exec(db, sqlite.UTF8String, nil, nil, &error);

    if (result == SQLITE_OK) {
        NSLog(@"删除数据成功");
    } else {
        NSLog(@"删除数据失败");
    }
}

/** 7.更改数据 */
+ (void)upDateWithStudent:(Student *)stu WhereName:(NSString *)name {

    //更新特定字段的SQLite语句
    NSString *sqlite = [NSString stringWithFormat:@"update t_student set name = '%@', age = '%ld' where name = '%@'",stu.name,stu.age,name];
    char *error = NULL;

    int result = sqlite3_exec(db, sqlite.UTF8String, nil, nil, &error);

    if (result == SQLITE_OK) {
        NSLog(@"修改数据成功");
    } else {
        NSLog(@"修改数据失败");
    }
}

/** 8.根据条件查询 */
+ (NSMutableArray *)selectWithAge:(NSInteger)age {

    //可变数组,用来保存查询到的数据
    NSMutableArray *array = [NSMutableArray array];

    //查询所有数据的SQLite语句
    NSString *sqlite = [NSString stringWithFormat:@"select * from t_student where age = '%ld'",age];

    //定义一个stmt存放结果集
    sqlite3_stmt *stmt = NULL;

    //执行
    int result = sqlite3_prepare(db, sqlite.UTF8String, -1, &stmt, NULL);

    if (result == SQLITE_OK) {
        NSLog(@"查询成功");

        //遍历查询到的所有数据,并添加到上面的数组中
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            Student *stu = [[Student alloc] init];
            //获得第1列的姓名,第0列是id
            stu.name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];
            //获得第2列的年龄
            stu.age = sqlite3_column_int(stmt, 2);
            [array addObject:stu];
        }
    } else {
        NSLog(@"查询失败");
    }

    //销毁stmt,防止内存泄漏
    sqlite3_finalize(stmt);
    return array;
}

/** 9.查询所有数据 */
+ (NSMutableArray *)selectStudent {

    //可变数组,用来保存查询到的数据
    NSMutableArray *array = [NSMutableArray array];

    //查询所有数据的SQLite语句
    NSString *sqlite = [NSString stringWithFormat:@"select * from t_student"];

    //定义一个stmt存放结果集
    sqlite3_stmt *stmt = NULL;

    //执行
    int result = sqlite3_prepare(db, sqlite.UTF8String, -1, &stmt, NULL);

    if (result == SQLITE_OK) {
        NSLog(@"查询成功");

        //遍历查询到的所有数据,并添加到上面的数组中
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            Student *stu = [[Student alloc] init];
            //获得第1列的姓名,第0列是id
            stu.name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];
            //获得第2列的年龄
            stu.age = sqlite3_column_int(stmt, 2);
            [array addObject:stu];
        }
    } else {
        NSLog(@"查询失败");
    }

    //销毁stmt,防止内存泄漏
    sqlite3_finalize(stmt);
    return array;
}

/** 10.删除表中的所有数据 */
+ (void)deleteAllData {
    NSString *sqlite = [NSString stringWithFormat:@"delete from t_student"];
    char *error = NULL;
    int result = sqlite3_exec(db, sqlite.UTF8String, nil, nil, &error);
    if (result == SQLITE_OK) {
        NSLog(@"清除数据库成功");
    } else {
        NSLog(@"清除数据库失败");
    }
}

/** 11.删除表 */
+ (void)dropTable {
    NSString *sqlite = [NSString stringWithFormat:@"drop table if exists t_student"];
    char *error = NULL;
    int result = sqlite3_exec(db, sqlite.UTF8String, nil, nil, &error);
    if (result == SQLITE_OK) {
        NSLog(@"删除表成功");
    } else {
        NSLog(@"删除表失败");
    }
}

/** 12.关闭数据库 */
+ (void)closeSqlite {
    int result = sqlite3_close(db);
    if (result == SQLITE_OK) {
        NSLog(@"数据库关闭成功");
    } else {
        NSLog(@"数据库关闭失败");
    }
}

依附SQLite的基本语句

  • 创建表:
    create table if not exists 表名 (字段名1, 字段名2…);

create table if not exists t_student (id integer primary key
autoincrement, name text not null, age integer)

  • 充实数据:
    insert into 表名 (字段名1, 字段名2, …) values(字段1的值,
    字段2的值, …);

insert into t_student (name,age) values (@”Jack”,@17);

  • 基于规则删除数据:
    delete from 表名 where 条件;

delete from t_student where name = @”Jack”;

  • 去除表中所有的数码:
    delete from 表名

delete from t_student

  • 遵照标准转移某个数据:
    update 表名 set 字段1 = ‘值1’, 字段2 = ‘值2’ where 字段1 =
    ‘字段1的眼前值’

update t_student set name = ‘lily’, age = ’16’ where name = ‘Jack’

  • 基于条件查找:
    select * from 表名 where 字段1 = ‘字段1的值’

select * from t_student where age = ’16’

  • 检索所有数据:
    select * from 表名

select * from t_student

  • 删除表:
    drop table 表名

drop table t_student

  • 排序查找:
    select * from 表名 order by 字段

select * from t_student order by age asc (升序,默认)
select * from t_student order by age desc (降序)

  • 限制:
    select * from 表名 limit 值1, 值2

select * from t_student limit 5, 10 (跳过5个,一共取10个数据)

SQLite3还有工作方面的利用,这里就不做验证了,下边的FMDB中会有事情的施用。

六、FMDB

FMDB封装了SQLite的C语言API,更加面向对象。
先是需要肯定的是FMDB中的多少个类。

FMDatabase:可以理解成一个数据库。

FMResultSet:查询的结果集合。

FMDatabaseQueue:运用多线程,可实施两个查询、更新。线程安全。

FMDB基本语法

询问:executeQuery: SQLite语句发号施令。

[db executeQuery:@"select id, name, age from t_person"]

另外的操作都是“更新”:executeUpdate: SQLite语句发号施令。

// CREATE, UPDATE, INSERT, DELETE, DROP,都使用executeUpdte
[db executeUpdate:@"create table if not exists t_person (id integer primary key autoincrement, name text, age integer)"]

FMDB的基本使用

在项目中导入FMDB框架和sqlite3.0.tbd,导入头文件。

1. 开辟数据库,并创立表

初始化FMDatabase:FMDatabase *db = [FMDatabase
databaseWithPath:filePath];
个中的filePath是提前准备好要存放数据的路径。

开辟数据库:[db open]

创制数据表:[db executeUpdate:@”create table if not exists t_person
(id integer primary key autoincrement, name text, age integer)”];

#import "ViewController.h"
#import <FMDB.h>

@interface ViewController ()

@end

@implementation ViewController{
    FMDatabase *db;
}

- (void)openCreateDB {

    //存放数据的路径
    NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
    NSString *filePath = [path stringByAppendingPathComponent:@"person.sqlite"];

    //初始化FMDatabase
    db = [FMDatabase databaseWithPath:filePath];

    //打开数据库并创建person表,person中有主键id,姓名name,年龄age
    if ([db open]) {
        BOOL success = [db executeUpdate:@"create table if not exists t_person (id integer primary key autoincrement, name text, age integer)"];
        if (success) {
            NSLog(@"创表成功");
        }else {
            NSLog(@"创建表失败");
        }
    } else {
        NSLog(@"打开失败");
    }
}

2. 插入数据

行使executeUpdate方法执行插入数据命令: [db executeUpdate:@”insert
into t_person(name,age) values(?,?)”,@”jack”,@17]

-(void)insertData {
    BOOL success = [db executeUpdate:@"insert into t_person(name,age) values(?,?)",@"jack",@17];
    if (success) {
        NSLog(@"添加数据成功");
    } else {
        NSLog(@"添加数据失败");
    }
}

3. 删减数据

去除姓名为lily的数目:[db executeUpdate:@”delete from t_person where
name = ‘lily'”]

-(void)deleteData {
    BOOL success = [db executeUpdate:@"delete from t_person where name = 'lily'"];
    if (success) {
        NSLog(@"删除数据成功");
    } else {
        NSLog(@"删除数据失败");
    }
}

4. 修改数据

把年纪为17岁的数额,姓名改为lily:[db executeUpdate:@”update
t_person set name = ‘lily’ where age = 17″]

-(void)updateData {
    BOOL success = [db executeUpdate:@"update t_person set name = 'lily' where age = 17"];
    if (success) {
        NSLog(@"更新数据成功");
    } else {
        NSLog(@"更新数据失败");
    }
}

5. 询问数据

实践查询语句,用FMResultSet接收查询结果:FMResultSet *set = [db
executeQuery:@”select id, name, age from t_person”]

遍历查询结果:[set next]

得到每条数的全名:NSString *name = [set stringForColumnIndex:1];

也足以如此得到每条数据的姓名:NSString *name = [result
stringForColumn:@”name”];

    FMResultSet *set = [db executeQuery:@"select id, name, age from t_person"];
    while ([set next]) {
        int ID = [set intForColumnIndex:0];
        NSString *name = [set stringForColumnIndex:1];
        int age = [set intForColumnIndex:2];
        NSLog(@"%d,%@,%d",ID,name,age);
    }
}

6. 删除表

删除指定表:[db executeUpdate:@”drop table if exists t_person”]

-(void)dropTable {
    BOOL success = [db executeUpdate:@"drop table if exists t_person"];
    if (success) {
        NSLog(@"删除表成功");
    } else {
        NSLog(@"删除表失败");
    }
}

FMDatabaseQueue基本使用

FMDatabase是线程不安全的,当FMDB数据存储想要使用多线程的时候,FMDatabaseQueue就派上用场了。

起首化FMDatabaseQueue的点子与FMDatabase类似

//数据文件路径
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [path stringByAppendingPathComponent:@"student.sqlite"];

//初始化FMDatabaseQueue
FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:filePath];

在FMDatabaseQueue中执行命令的时候也是卓殊有利,直接在一个block中开展操作

-(void)FMDdatabaseQueueFunction {

    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    //数据文件路径
    NSString *filePath = [path stringByAppendingPathComponent:@"student.sqlite"];
    //初始化FMDatabaseQueue
    FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:filePath];

    //在block中执行SQLite语句命令
    [dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
        //创建表
        [db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer)"];
        //添加数据
        [db executeUpdate:@"insert into t_student(name,age) values(?,?)",@"jack",@17];
        [db executeUpdate:@"insert into t_student(name,age) values(?,?)",@"lily",@16];
        //查询数据
        FMResultSet *set = [db executeQuery:@"select id, name, age from t_student"];
        //遍历查询到的数据
        while ([set next]) {
            int ID = [set intForColumn:@"id"];
            NSString *name = [set stringForColumn:@"name"];
            int age = [set intForColumn:@"age"];
            NSLog(@"%d,%@,%d",ID,name,age);
        }

    }];
}

FMDB中的事务

怎么着是工作?

作业(Transaction)是不可分割的一个完完全全操作,要么都履行,要么都不实施。
举个例子,幼儿园有20位小朋友由民办助教团队出去郊游,返校的时候,所有人依次登中将车,那时候如果有一位儿童没有上车,车也是无法出发的。所以固然19人都上了车,也等于0人上车。20人是一个总体。
自然这一个例子可能不是很精准。

FMDB中有工作的回滚操作,也就是说,当一个全部业务在实践的时候出了少数小题目,则执行回滚,之后这套事务中的所有操作将完整无效。

下面代码中,利用工作循环向数据库中添加2000条数据,如果在添加的过程中出现了部分题材,由于实施了*rollback
= YES的回滚操作,数据库中一个数额都不会冒出。
假若第2000条数据的增长出了问题,哪怕此前曾经添加了1999条数据,由于举行了回滚,数据库中仍旧一个数码都尚未。

//数据库路径
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [path stringByAppendingPathComponent:@"student.sqlite"];

//初始化FMDatabaseQueue
FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:filePath];

//FMDatabaseQueue的事务inTransaction
[dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        //创建表
        [db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer)"];
        //循环添加2000条数据
        for (int i = 0; i < 2000; i++) {
            BOOL success = [db executeUpdate:@"insert into t_student(name,age) values(?,?)",@"jack",@(i)];
            //如果添加数据出现问题,则回滚
            if (!success) {
                //数据回滚
                *rollback = YES;
                return;
            }
        }
    }];

七、Core Data

Core
Data有着图形化的操作界面,并且是操作模型数据的,更加面向对象。当你打探并了解它的时候,相信您会欣赏上这一个数据库存储框架。

拔取Core Data迅速实现多少存储

1. 图形化成立模型

创建项目标时候,勾选下图中的Use Core
Data选项,工程中会自动创制一个数据模型文件。当然,你也得以在付出中自己手动创制。

自行创建模型文件

下图就是机动创立出来的文本

创建出来的公文

假定没有勾选,也得以在此处手动创造。

手动创设

点击Add
Entity之后,非常一张数据表。表的名号自己在下边定义,注意首字母要大写。
在界面中还足以为数据实体添加属性和关联属性。

开创一个数据表

Core Data属性帮助的数据类型如下

数据类型

编译之后,Xcode会自动生成Person的实体代码文件,并且文件不会突显在工程中,假设下图中左侧Codegen选用Manual/None,则Xcode就不会自动生成代码,我们得以友善手动生成。

6.png

手动生成实体类代码,选中CoreDataTest.xcdatamodeld文件,然后在Mac菜单栏中选择Editor,如下图所示。一路Next就可以了。
一经没有选取Manual/None,依旧举办手动创立的话,则会与系统自动创制的文本暴发争持,这点需要专注。
您也可以毫无挑选Manual/None,直接行使系统创立好的NSManagedObject,同样会有4个公文,只是在工程中是看不到的,使用的时候一向导入#import
“Person+CoreDataClass.h”头文件就可以了。

手动创设NSManagedObject

手动创制出来的是这么4个公文

10.png

还要小心编程语言的选项,斯维夫特(Swift)(Swift)或OC

编程语言

2.Core Data堆栈的介绍与使用

下边要做的就是对Core Data举行先导化,实现本地数据的保留。
内需采纳的类有五个:

NSManagedObjectModel 数据模型的结构信息
NSPersistentStoreCoordinator 数据持久层和目标模型协调器
NSManagedObjectContext 对象的前后文managedObject 模型

如下图所示,一个context内得以有多少个模型对象,但是在大部的操作中只设有一个context,并且有着的对象存在于那多少个context中。
目标和他们的context是相关联的,每个被管理的目的都精通自己属于哪个context,每个context都知晓自己管理着哪些对象。

Core Data从系统读或写的时候,有一个持久化存储协调器(persistent store
coordinator),并且这些协调器在文件系统中与SQLite数据库交互,也总是着存放在模型的内外文Context。

Core Data

下图是相比较常用的艺术:

Core Data堆栈

上面大家来一步步落实Core Data堆栈的创办。

  • 第一在AppDelegate中定义一个NSManagedObjectModel属性。然后利用懒加载来创立NSManagedObjectModel对象。并且要留心成立时候的后缀用momd,代码如下:
//创建属性
@property (nonatomic, readwrite, strong) NSManagedObjectModel *managedObjectModel;

//懒加载
- (NSManagedObjectModel *)managedObjectModel {
    if (!_managedObjectModel) {
        //注意扩展名为 momd
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreDataTest" withExtension:@"momd"];
        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    }
    return _managedObjectModel;
}
  • 创设协调器NSPersistentStoreCoordinator,同样的先在AppDelegate中来一个性能,然后懒加载。
//属性
@property (nonatomic, readwrite, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;

//懒加载
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (!_persistentStoreCoordinator) {

        //传入之前创建好了的Model
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];

        //指定sqlite数据库文件
        NSURL *sqliteURL = [[self documentDirectoryURL] URLByAppendingPathComponent:@"CoreDataTest.sqlite"];

        //这个options是为了进行数据迁移用的,有兴趣的可以去研究一下。
        NSDictionary *options=@{NSMigratePersistentStoresAutomaticallyOption:@(YES),NSInferMappingModelAutomaticallyOption:@(YES)};
        NSError *error;

        [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                  configuration:nil
                                                            URL:sqliteURL
                                                        options:options
                                                          error:&error];
        if (error) {
            NSLog(@"创建协调器失败: %@", error.localizedDescription);
        }
    }
    return _persistentStoreCoordinator;
}


//获取document目录
- (nullable NSURL *)documentDirectoryURL {
    return [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
}
  • 创设NSManagedObjectContext,同样的是性质+懒加载。
//属性
@property (nonatomic, readwrite, strong) NSManagedObjectContext *context;

//懒加载
- (NSManagedObjectContext *)context {
    if (!_context) {

        _context = [[NSManagedObjectContext alloc ] initWithConcurrencyType:NSMainQueueConcurrencyType];

        // 指定协调器
        _context.persistentStoreCoordinator = self.persistentStoreCoordinator;
    }
    return _context;
}

3.运用Core Data对数据开展增删改查

  • 加上数量
    利用NSEntityDesctiption类的一个方法创制NSManagedObject对象。参数一是实体类的名字,参数二是事先创立的Context。
    为目标赋值,然后存储。
@implementation ViewController {
    NSManagedObjectContext *context;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    //拿到managedObjectContext
    context = [AppDelegate new].context;

    //运用NSEntityDescription创建NSManagedObject对象
    Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    //为对象赋值
    person.name = @"Jack";
    person.age = 17;
    NSError *error;
    //保存到数据库
    [context save:&error];
}
  • 查询数据
    Core Data从数据库中查询数据,会用到两个类:

NSFetchRequest:一条查询请求,相当于 SQL 中的select语句
NSPredicate:谓词,指定一些询问条件,相当于 SQL 中的where
NSSortDescriptor:点名排序规则,相当于 SQL 中的 order by

NSFetchRequest中有两个特性:

predicate:是NSPredicate对象
sortDescriptors:它是一个NSSortDescriptor数组,数组中后面的先期级比前边高。可以有两个排列规则。

    //更多NSFetchRequest属性
    fetchLimit:结果集最大数,相当于 SQL 中的limit
    fetchOffset:查询的偏移量,默认为0
    fetchBatchSize:分批处理查询的大小,查询分批返回结果集
    entityName/entity:数据表名,相当于 SQL中的from
    propertiesToGroupBy:分组规则,相当于 SQL 中的group by
    propertiesToFetch:定义要查询的字段,默认查询全部字段

设置好NSFetchRequest之后,调用NSManagedObjectContext的executeFetchRequest方法,就会回到结果集了。

    //Xcode自动创建的NSManagedObject会生成fetchRequest方法,可以直接得到NSFetchRequest
    NSFetchRequest *fetchRequest = [Person fetchRequest];
    //也可以这样获得:[NSFetchRequest fetchRequestWithEntityName:@"Student"];

    //谓词
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"age == %@", @(16)];

    //排序
    NSArray<NSSortDescriptor *> *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES]];

    fetchRequest.sortDescriptors = sortDescriptors;

    //运用executeFetchRequest方法得到结果集
    NSArray<Person *> *personResult = [context executeFetchRequest:fetchRequest error:nil];

Xcode自己有一个NSFetchRequest的code snippet,“fetch”,出现的结果如下图。

snippet

NSFetchRequest的code snippet: “fetch”

  • 立异数据
    履新数据相比简单,查询出来需要修改的数量将来,间接修改值,然后用context
    save就足以了。
for (Person *person in personResult) {
        //直接修改
        person.age = 26;
    }

//别忘了save一下
[context save:&error];
  • 删去数据
    询问出来需要删除的多寡将来,调用 NSManagedObjectContext
    的deleteObject方法就足以了。
for (Person *person in personResult) {
        //删除数据
        [context deleteObject:person];
    }
//别忘了save
[context save:&error];

至今Core Data的基础内容就讲完了。


后记:

iOS中有多种数目持久化的措施,要遵照现实意况接纳最合适的技术。

推荐简单又好用的分类集合:WHKit
github地址:https://github.com/remember17

网站地图xml地图