iOS:CoreData的使用

一、CoreData的简单理解

  • CoreData是一个模型层的技术,也是一种持久化技术,它能将模型对象的状态持久化到磁盘里,我们不需要使用SQL语句就能对它进行操作。

  • CoreData不是一个数据库但是可以使用数据库来存储数据,也可以使用其他方式,比如:数据库文件XML二进制文件内存等。CoreData提供了对象-关系映射(ORM) 功能。能够实现数据库数据和OC对象的相互转换,在这个转换过程中我们不需要编写任何SQL语句。

  • 为什么要使用CoreData
    极大的减少Model层的代码量
    优化了使用SQLite时候的性能
    提供了可视化设计

二、CoreData的使用

1、创建步骤
  • 如果你是一个新的项目,可以创建项目的时候选择CoreData如图
  • 如果是老的项目之前没有使用CoreData ,则按照如下的步骤:

第一步:先创建.xcdatamodeld文件(右键 -> New File -> iOS -> Core Data ->Data Model)

第二步:命名然后创建(这里的名字要在后面操作的时候使用,要命好名字)

成功

2、创建CodeDataMode实体,并在实体中添加我们需要的键值如图
3、创建实体关联文件
  • 自动创建实体关联文件

创建好实体对象XXX.xcdatamodeld之后,右侧属性栏Code Generation下面的Language默认为Swift,这里使用OC,就改成Objective-CCodegen默认为Class Definition,无需更改。通过XcodeBuild会自动生成对应的实体关联文件,但是这些文件不会在目录中显示出来,对应的格式为:

//格式
实体名(表名)+CoreDataClass.h
实体名(表名)+CoreDataClass.m
实体名(表名)+CoreDataProperties.h
实体名(表名)+CoreDataProperties.m
//例如,实体名(表名)为Video,对应的关联文件为:
CoreDataMode+CoreDataClass.h
CoreDataMode+CoreDataClass.m
CoreDataMode+CoreDataProperties.h
CoreDataMode+CoreDataProperties.m

使用对应的实体时,导入对应的头文件即可,例如:

#import "CoreDataMode+CoreDataProperties.h"
  • 手动创建实体关联文件

实体对象XXX.xcdatamodeld里面的Codegen一定得设置为Manual/None,否则报文件重复错误,选中实体,点击Editor,点击Create NSManagedObject Subclass…生成实体关联文件:

手动创建好关联文件之后,目录如下图所示,用的时候导入对应的Properties头文件即可:

4 、使用相应的类
  • NSManagedObjectContext

数据库操作:NSManagedObjectContext被管理的对象上下文(对数据直接操作)
NSManagedObjectContext:等同于一个容器,用来存储从数据库中转换出来的所有的OC对象。我们的增删改查操作直接对这个类使用来获得或者修改需要的OC对象,它能够调用NSPersistentStoreCoordinator类实现对数据库的同步,这个对象有点像SQLite对象(用来管理.xcdatamodeld中的数据)。
负责数据和应用库之间的交互(CRUD,即增删改查、保存等接口都是用这个对象调用)。
每个NSManagedObjectContext和其他NSManagedObjectContext都是完全独立的。
所有的NSManagedObject(个人理解:实体数据)都存在于NSManagedObjectContext中。
每个NSManagedObjectContext都知道自己管理着哪些NSManagedObject(实体数据)
可以通过TA去访问底层的框架对象集合,这些对象集合统称为持久化堆栈(persistence stack)——它在应用程序和外部数据存储的对象之间提供访问通道

  • NSManagedObject

NSManagedObject的工作模式有点类似于NSDictionary对象,通过键-值对来存取所有的实体属性。NSManagedObject:数据库中的数据转换而来的OC对象
setValue:forkey:存储属性值(属性名为key);
valueForKey:获取属性值(属性名为key)。
每个NSManagedObject都知道自己属于哪个NSManagedObjectContext用于插入数据使用:获得实体,改变实体各个属性值,保存后就代表插入

  • NSEntityDescription

NSEntityDescription用来描述实体(Entity) 表格结构: 相当于数据库中的一个表,TA描述一种抽象数据类型
通过Core Data从数据库中取出的对象,默认情况下都是NSManagedObject对象。
+insertNewObjectForEntityForName:inManagedObjectContext: 工厂方法,根据给定的Entity描述,生成相应的NSManagedObject对象,并插入到ManagedObjectContext
LHModel * model = [NSEntityDescription insertNewObjectForEntityForName:@“CoreDataMode” inManagedObjectContext:self.managedObjectContext];
通过上面的代码可以得到model这个表的实例,然后可以使用这个实例去为表中的属性赋值
model.title = @“标题”;
model.content = @“内容”;

  • NSPersistentStoreCoordinator

NSPersistentStoreCoordinator持久化存储库,CoreData的存储类型(比如SQLite数据库就是其中一种)。
用来将对象管理部分和持久化部分捆绑在一起,负责相互之间的交流
用来设置CoreData存储类型和存储路径
使用Core Data document类型的应用程序,通常会从磁盘上的数据文中中读取或存储数据,这写底层的读写就由Persistent Store Coordinator来处理。一 般我们无需与它直接打交道来读写文件,Managed Object Context在背后已经为我们调用Persistent Store Coordinator做了这部分工作
NSPersistentStoreCoordinator:通过解析结果去实现数据库和OC对象之间的相互转换,主要是操作数据库的,我们一般用不上,由系统处理

  • NSManagedObjectModel

NSManagedObjectModel Core Data的模型文件,有点像SQLite的.sqlite文件(个人理解:表示一个.xcdatamodeld文件)应用程序的数据模型,数据库中所有表格和他们之间的联系
NSManagedObjectModel:负责读取解析.momod文件
NSManagedObjectModel * model = [self managedObjectModel];//获取实例
NSDictionary * entities = [model entitiesByName];//entitiesByName得到所有的表的名字
NSEntityDescription * entity = [entities valueForKey:@“CoreDataModel”];//从里面找出名为Student的表

5、具体实现 创建CoreDataManager 类

CoreDataManager.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface CoreDataManager : NSObject

/**
 * 上下文  容器
 * 存放的是 所有从数据库中取出的转换成OC对象
 */
@property (strong, nonatomic) NSManagedObjectContext * managedObjectContext;

/* 读取解析 .momd文件中的内容 */
@property (strong, nonatomic) NSManagedObjectModel * managedObjectModel;

/* 连接的类,处理数据库数据和OC数据底层的相互转换 */
@property (strong, nonatomic) NSPersistentStoreCoordinator * persistentStoreCoordinator;

/**
 单例
 @return CoreDataManager
 */
+ (instancetype)sharedInstance;
/**
 插入数据
 @param dict 字典中的键值对必须要与实体中的每个名字一一对应
 @param success 成功回调
 @param fail 失败回调
 */
- (void)insertNewEntity:(NSDictionary *)dict success:(void(^)(void))success fail:(void(^)(NSError *error))fail;

/**
 查询数据
 @param selectKays 数组高级排序(数组里存放实体中的key,顺序按自己需要的先后存放即可),实体key来排序
 @param isAscending 升序降序
 @param filterString 查询条件
 @param filterString 成功回调
 @param fail 失败回调
 */
- (void)selectEntity:(NSArray *)selectKays ascending:(BOOL)isAscending filterString:(NSString *)filterString success:(void(^)(NSArray *results))success fail:(void(^)(NSError *error))fail;

/**
 删除数据
 @param model NSManagedObject
 @param success 成功回调
 @param fail 失败回调
 */
- (void)deleteEntity:(NSManagedObject *)model success:(void(^)(void))success fail:(void(^)(NSError *error))fail;

/**
 更新数据
 @param success 成功回调
 @param fail 失败回调
 */
- (void)updateEntity:(void(^)(void))success fail:(void(^)(NSError *error))fail;

@end

CoreDataManager.m

#import "CoreDataManager.h"

//获取CoreData的.xcdatamodel文件的名称
static NSString * const coreDataModelName = @"CoreDataText";
//获取CodeData
static NSString * const coreDataEntityName = @"CoreDataMode";
//数据库
static NSString * const sqliteName = @"CoreDataMode.sqlite";
@interface CoreDataManager()

@end

@implementation CoreDataManager
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

static CoreDataManager *coreDataManager = nil;
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        coreDataManager = [[CoreDataManager alloc] init];
    });
    return coreDataManager;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (coreDataManager == nil) {
            coreDataManager = [super allocWithZone:zone];
        }
    });
    return coreDataManager;
}

- (void)insertNewEntity:(NSDictionary *)dict success:(void (^)(void))success fail:(void (^)(NSError *))fail {
    if (!dict||dict.allKeys.count == 0) return;
    // 通过传入上下文和实体名称,创建一个名称对应的实体对象(相当于数据库一组数据,其中含有多个字段)
    NSManagedObject * entity = [NSEntityDescription insertNewObjectForEntityForName:coreDataEntityName inManagedObjectContext:self.managedObjectContext];
    // 实体对象存储属性值(相当于数据库中将一个值存入对应字段)
    for (NSString *key in [dict allKeys]) {
        [entity setValue:[dict objectForKey:key] forKey:key];
    }
    // 保存信息,同步数据
    NSError *error = nil;
    BOOL result = [self.managedObjectContext save:&error];
    if (!result) {
        NSLog(@"添加数据失败:%@",error);
        if (fail) {
            fail(error);
        }
    } else {
        NSLog(@"添加数据成功");
        if (success) {
            success();
        }
    }
}

- (void)deleteEntity:(NSManagedObject *)model success:(void (^)(void))success fail:(void (^)(NSError *))fail {
    // 传入需要删除的实体对象
    [self.managedObjectContext deleteObject:model];
    // 同步到数据库
    NSError *error = nil;
    [self.managedObjectContext save:&error];
    if (error) {
        NSLog(@"删除失败:%@",error);
        if (fail) {
            fail(error);
        }
    } else {
        NSLog(@"删除成功");
        if (success) {
            success();
        }
    }
}

- (void)selectEntity:(NSArray *)selectKays ascending:(BOOL)isAscending filterString:(NSString *)filterString success:(void (^)(NSArray *))success fail:(void (^)(NSError *))fail {
    // 1.初始化一个查询请求
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    // 2.设置要查询的实体
    NSEntityDescription *desc = [NSEntityDescription entityForName:coreDataEntityName inManagedObjectContext:self.managedObjectContext];
    request.entity = desc;
    // 3.设置查询结果排序
    if (selectKays&&selectKays.count>0) { // 如果进行了设置排序
        NSMutableArray *array = [NSMutableArray array];
        for (NSString *key in selectKays) {
            /**
             *  设置查询结果排序
             *  sequenceKey:根据某个属性(相当于数据库某个字段)来排序
             *  isAscending:是否升序
             */
            NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:key ascending:isAscending];
            [array addObject:sort];
        }
        if (array.count>0) {
            request.sortDescriptors = array;// 可以添加多个排序描述器,然后按顺序放进数组即可
        }
    }
    // 4.设置条件过滤
    if (filterString) { // 如果设置了过滤语句
        NSPredicate *predicate = [NSPredicate predicateWithFormat:filterString];
        request.predicate = predicate;
    }
    // 5.执行请求
    NSError *error = nil;
    NSArray *objs = [self.managedObjectContext executeFetchRequest:request error:&error]; // 获得查询数据数据集合
    if (error) {
        NSLog(@"失败");
        if (fail) {
            fail(error);
        }
    } else{
        NSLog(@"成功");
        if (success) {
            success(objs);
        }
    }
}

- (void)updateEntity:(void (^)(void))success fail:(void (^)(NSError *))fail {
    NSError *error = nil;
    [self.managedObjectContext save:&error];
    if (error) {
        NSLog(@"删除失败:%@",error);
        if (fail) {
            fail(error);
        }
    } else {
        if (success) {
            success();
        }
    }
}

#pragma 懒加载
//managedObjectModel 属性的getter方法
-(NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) return _managedObjectModel;
    //.xcdatamodeld文件 编译之后变成.momd文件  (.mom文件)
    NSURL * modelURL = [[NSBundle mainBundle] URLForResource:coreDataModelName withExtension:@"momd"];
    //把文件的内容读取到managedObjectModel中
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

//Coordinator 调度者负责数据库的操作 创建数据库 打开数据 增删改查数据
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
    //根据model创建了persistentStoreCoordinator
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    // 设置数据库存放的路径
    NSURL * storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:sqliteName];
    NSError * error = nil;
    //如果没有得到数据库,程序崩溃
    /*
     持久化存储库的类型:
     NSSQLiteStoreType  SQLite数据库
     NSBinaryStoreType  二进制平面文件
     NSInMemoryStoreType 内存库,无法永久保存数据
     虽然这3种类型的性能从速度上来说都差不多,但从数据模型中保留下来的信息却不一样
     在几乎所有的情景中,都应该采用默认设置,使用SQLite作为持久化存储库
     */
    // 添加一个持久化存储库并设置类型和路径,NSSQLiteStoreType:SQLite作为存储库
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
    {
        if (error) {
            NSLog(@"添加数据库失败:%@",error);
        } else {
            NSLog(@"添加数据库成功");
        }
        NSLog(@"错误信息: %@, %@", error, [error userInfo]);
    }
    return _persistentStoreCoordinator;
}

-(NSURL *)applicationDocumentsDirectory
{
    //获取沙盒路径下documents文件夹的路径 NSURL   (类似于search)
    NSLog(@"%@",[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject].absoluteString);
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

//容器类 存放OC的对象
-(NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil)  return _managedObjectContext;
    NSPersistentStoreCoordinator * coordinator = [self persistentStoreCoordinator];
    if (!coordinator)
    {
        return nil;
    }
    //创建context对象
    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    //让context和coordinator关联   context可以对数据进行增删改查功能   // 设置上下文所要关联的持久化存储库
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    return _managedObjectContext;
}
@end

6、具体使用
  • 增 insert

     NSDictionary * dict = @{@"title":@"title2",@"content":@"content2"};
     [[CoreDataManager sharedInstance] insertNewEntity:dict success:^{
         NSLog(@"成功");
     } fail:^(NSError *error) {
         NSLog(@"失败");
     }];
    
    

运行结果 :

2018-11-12 13:25:06.730756+0800 CoreDataText[1600:1182994] 添加数据成功

数据库中显示:


在这里插入图片描述
  • 查询 select

     [[CoreDataManager sharedInstance] selectEntity:nil ascending:YES filterString:nil success:^(NSArray *results)            {
         NSLog(@"数据---%@",results);
     } fail:^(NSError *error) {
    
     }];
    
    

运行结果:


在这里插入图片描述
在这里插入图片描述
  • 改 updata

    [[CoreDataManager sharedInstance] selectEntity:nil ascending:YES filterString:nil success:^(NSArray *results) {
         NSLog(@"数据---%@",results);
         if (results.count>0) {
              //更新某个
              NSManagedObject * model = [results firstObject];
             [model setValue:@"xiugaile title" forKey:@"title"];
             [model setValue:@"xiugaile content" forKey:@"content"];
             [[CoreDataManager sharedInstance] updateEntity:^{
             } fail:^(NSError *error) {
             }];
         }
    } fail:^(NSError *error) {
    
    }];
    
    

    运行结果 :


    在这里插入图片描述
    在这里插入图片描述
  • 删除 delete

    //删除某个 
    NSString * filterString = [NSString stringWithFormat:@"title = 'title'"];
    [[CoreDataManager sharedInstance] selectEntity:nil ascending:YES filterString:filterString success:^(NSArray *results) {
        NSLog(@"数据---%@",results);
        if (results.count>0) {
            //删除某个
            [[CoreDataManager sharedInstance] deleteEntity:[results firstObject] success:^{
            } fail:^(NSError *error) {
            }];
          }
    
     } fail:^(NSError *error) {
    }];
    //全部删除
    [[CoreDataManager sharedInstance] selectEntity:nil ascending:YES filterString:nil success:^(NSArray *results) {
         NSLog(@"数据---%@",results);
        //全部删除
        for (NSManagedObject *obj in results){
            [[CoreDataManager sharedInstance] deleteEntity:obj success:^{
            } fail:^(NSError *error) {
            }];
        }
    } fail:^(NSError *error) {
    
    }];
    
    

    运行结果:


    在这里插入图片描述
    在这里插入图片描述

参考:
https://blog.csdn.net/u013983033/article/details/83378838
https://www.jianshu.com/p/e98daf9fec78
https://www.jianshu.com/p/332cba029b95
https://www.jianshu.com/p/e676ade9fcc6
https://www.jianshu.com/p/c640dc6fd0e0

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