CoreData

前言

  • 这篇记录主要记录我学习Core Data的过程,其中在原始记录上做了很多次的补充,并且已经标注出来,以此来表示学习是循序而进的一个过程。
  • 这个记录适合想初识Core Data的同伴。
  • 我真的想把它称为史上最人性化的Core Data总结,所谓人性化就是用的是小白能听懂的语言。
  • 文章很长,或许有点枯燥,其实我写的时候是充满了激情的,语言或许有点搞笑。
  • 最后,请耐心阅读。

内容概要

  • Xcode 8 和Xcode 10 创建时的步骤。
  • Xcode 10 比Xcode 8 多的内容。
  • 增删改查的具体代码
使用Core Data的步骤:(Xcode 10)

第一种,从项目开始就知道我要使用Core Data

  1. 创建项目的时候把Use CoreData 勾上

  2. 会生成.xcdatamodeld文件.
    关于这个名字我是这么理解的:Xcode 的 Core Data 的 model
    简写就是xc(xcode)data(coredata)modeld(这个词网络义是“建模”),这样这个名字就很容易被记住了。我的理解就是他的文件名,Core Data的模型

  • 第二次补充:我将“Core Data的模型”画掉了,因为第一遍理解的不对。xcdatamodeld是关于模型的文件,可以利用这个文件生成模型的对象,再利用模型的对象生成实例。
  1. 点击界面最下边的add entity,可以添加一个实体。双击可以改实体的名字,名字第一个英文字母要大写,否则会报警告,看着多难受,而且也会没有办法进行下一步,强制要求你大写,所以还是乖乖的大写好了。
    关于这个实体的解释,我把它看做一个表。

  2. xcdatamodeld文件里面有3个部分:Attributes,relationships , Fetched Proerties.
    Attributes:属性,可以理解为一张表的属性。就比如年龄,身高。
    relationships:我目前只知道是关系,表和表的关系,有一对多,多对多。但是怎么设置我不知道。后面会了再补充。
    Fetched Proerties:我目前是一点也不知道,这么鬼东西。

  3. .找到并执行Create NSManagedObject SubClass,创建 NSManagedObject实体类,会有4个分类。他们叫做托管实体类,这是什么东东,我真的不知道。

  4. 大神总结的几个类的含义,我反正没有理解。
    NSManagedObjectContext 上下文
    NSManagedObjectModel 数据模型
    NSPersistentStoreCoordinator 使用它来添加数据库,设置储存有关的内容
    NSManagedObject 被管理的数据记录
    NSFetchRequest 数据请求
    NSEntityDescription 表格实体结构


第二种:自己项目写一半了才知道必须要创建一个coredata了
1.先去commond+n创建一个Data Model文件
2.我们需要手动写代码,创建数据库,关联数据库,还要加在上下文中。
先将手动创建的代码展示出来

    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *sqlPath = [docStr stringByAppendingPathComponent:@"coreData.sqlite"];
    NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
    NSError *error = nil;
    [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:&error];
    if (error) {
        NSLog(@"添加数据库失败:%@",error);
    } else {
        NSLog(@"添加数据库成功");
    }
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    context.persistentStoreCoordinator = store;
    _context = context;
}

我对代码的解读:

我们要创建数据库肯定得根据我们的模型去创建,包括数据库名字里面有哪些字段属性。
这时候我们的.xcdatamodeld文件的作用就是这个,模型的信息全被保存到一个后缀为momd的文件中。NSManagedObjectModel这个类就用到了:首先找到.momd文件的路径,根据路径找到模型文件利用NSManagedObjectModel类将模型文件中的信息真的变成模型对象。再根据模型去创建对象。

之后就考虑到了数据的持久化,所谓持久化,就是写个sqlite文件,将数据保存起来,不过,保存下来的数据都是按照模型的标准保存的。我们用到的类是NSPersistentStoreCoordinator ,调用
[store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:&error];
方法。传入的参数就是采用什么样的存储方式,以及对应文件的URL 。
基本都是用sqlite,传入sqlite url地址。就能根据模型路径创建好sqlite文件。

最后一步是创建上下文,将上下文关联持久化助理(其实就是关联好了数据库)。

使用Core Data的步骤:(Xcode 8)

勾选了Core Data 后,将原来的.xcdatamodeld文件删掉,自己手动创建一个data model文件,后面和Xcode10手动创建的步骤一样。

Xcode 10 比 Xcode 8 多的内容
介绍

Xcode 10相比于Xcode 8 多了一个类:NSPersistentContainer
咱去查查它的英文意思,persistent(不断的,坚持的)Container(容器)总结下来就是持久容器。
但是大神对这个类的总结是:封装了应用程序中的CoreData Stack(核心数据栈堆),简化了创建和管理的核心堆栈的数据处理创建。关于核心数据栈堆???什么鬼?

  • 第二次补充:NSPersistentContainer 是一个容器,封装了应用程序中的CoreData Stack
    实例化有4中方法:2种类方法,2种对象方法
    //name 表示数据库的名称,这个方法其实直接帮我们创建了名称为name一个数据库,并且关联到NSPersistentContainer //model是根据模型来创建的数据库
    +(instancetype)persistentContainerWithName:(NSString *)name;
    +(instancetype)persistentContainerWithName:(NSString *)name managedObjectModel:(NSManagedObjectModel *)model
    对象方法和类方法是一样的,无非就把+换成-
    可访问属性
    //返回沙盒中存储数据库的文件夹URL路径
    + (NSURL *)defaultDirectoryURL;
    //只读属性,当前NSPersistentContainer容器的名称
    @property (copy, readonly) NSString *name;
    //只读属性,自动生成的管理对象上下文,这个上下文默认的操作类型是NSMainQueueConcurrencyType主线程
    ***** NSManagedObjectContext 加5颗星很重要*****
    @property (strong, readonly) NSManagedObjectContext *viewContext;
    //只读属性,对象管理模型
    @property (strong, readonly) NSManagedObjectModel *managedObjectModel;
    //只读属性,存储调度器
    @property (strong, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
    配置信息
    //加载存储器,此方法必须要调用,否则无法存储数据 //block中NSPersistentStoreDescription用于描述生成的存储器信息,如:数据库文件路径、存储类型等 NSError用于描述加载存储器是否成功或失败信息
    - (void)loadPersistentStoresWithCompletionHandler:(void (^)(NSPersistentStoreDescription *, NSError * _Nullable))block;

//返回一个基于多线程的管理对象上下文,我们无需关心多线程的内部实现以及线程安全,由NSPersistentContainer新创建一个
//此方法非常牛逼,调用这个方法之后,对返回的上下文做一些数据的处理都是在子线程中完成的,可以用于处理对数据库进行大量数据操作的场景
(NSManagedObjectContext *)newBackgroundContext NS_RETURNS_RETAINED;

源码分析

在APPDelegate.h文件中新增了

@property (readonly, strong) NSPersistentContainer *persistentContainer;

- (void)saveContext;

在APPDelegate.m中新增了persistentContainer get方法

- (NSPersistentContainer *)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            //创建一个数据库的容器
            // name:表示保存的数据库文件名称
            // 使用此方法创建NSPersistentContainer,默认模型文件名称为name
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"sqlite"];
            //加载存储器,此方法必须要调用,否则无法存储数据
          //block中NSPersistentStoreDescription用于描述生成的存储器信息,如:数据库文件路径、存储类型等  NSError用于描述加载存储器是否成功或失败信息
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                  //语言的函数,强制退出
                    abort();
                }
            }];
        }
    }
    return _persistentContainer;
}

- (void)saveContext {
    //   取出上下文
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    NSError *error = nil;
    if ([context hasChanges] && ![context save:&error]) {
     
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        abort();
    }
}
具体的增删改查的代码
   Dog *dog = [NSEntityDescription insertNewObjectForEntityForName:@"Dog" inManagedObjectContext:_app.persistentContainer.viewContext];
   static NSInteger index = 0;
   dog.name = [NSString stringWithFormat:@"tom%ld",index++];
   dog.sex = @"公";
   dog.age = [NSString stringWithFormat:@"%uyears",arc4random() % 15];
   [_app saveContext];

关于NSEntityDescription
每一个data model对应一个NSEntityDescription对象,NSEntityDescription可以根据传入的EntityName找到对应的模型生成一个NSManagedObject实体。
最后要调用APPDelegate的saveContext方法。


    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dog" inManagedObjectContext:_app.persistentContainer.viewContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entity];
    NSArray *array = [_app.persistentContainer.viewContext executeFetchRequest :request error:nil];
    for (Dog *dog in array) {
        NSLog(@"dog name:%@",dog.name);
    }
} 

关于NSFetchRequest类,在介绍这个之前要先介绍一下NSPredicate这个类
它叫做“谓词”,可以对数组根据指定条件筛选最后以数组的形式返回。
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"过滤条件"];
有两种过滤方法:1.将数组单个取出过滤。2.对数组过滤。代码分别为:
[predicate evaluateWithObject:model];
NSArray *results = [array filteredArrayUsingPredicate:predicate];
过滤条件想深入了解请戳这里https://blog.csdn.net/fengsh998/article/details/8125263

NSFetchRequest的实例描述用于从持久性存储检索数据的搜索条件。
常用方法:

-setEntity:设置你要查询的数据对象的类型(Entity)你需要创建一个NSEntityDescription对象
-setPredicate:设置查询条件,你需要创建一个NStPredicate对象
-setFetchLimit:设置最大查询对象数目,你需要创建一个NStPredicate对象
-setSortDescriptors:设置查询结果的排序方法,你需要创建一个NSSortDescriptors对象

好的,再来补充一下NSSortDescriptors
很多集合都提供排序的方法EM:NSSet,NSArray,NSMutableArray,NSOrderedSet,NSMutableOrderedSet

NSSet
-(NSArray *)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors
NSArray
-(NSArray *)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors;
NSMutableArray
-(void)sortUsingDescriptors:(NSArray *)sortDescriptors;
NSOrderedSet
-(NSArray *)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors
NSMutableOrderedSet
-(void)sortUsingDescriptors:(NSArray *)sortDescriptor

例子:

NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES];
   //key : 排序key, 某个对象的属性名称,ascending : 是否升序, YES-升序, NO-降序
 [peoples sortUsingDescriptors:@[sort]];
    
    // 输出排序结果,如果需要多个排序条件,再创建一个NSSortDescriptor对象,在@[sort]后添加例如sort1.
    for (People *people in peoples) {
        NSLog(@"age: %d,height: %f name: %@", people.age,people.height, people.name);
    }

但是,我又遇到没有见过的NSOrderedSet,NSMutableOrderedSet

  • NSSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
  • NSSet不保证元素的添加顺序,顺序有可能发生变化。
  • 与NSArray相比最大区别是元素没有索引
  • 元素不可重复
    想详细了解戳这里https://www.jianshu.com/p/db6395cd9bfd

-(void)updateCoreData{
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dog" inManagedObjectContext:_app.persistentContainer.viewContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.entity = entity;
    
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@",@"tom0"];
    request.predicate = predicate;
    
    NSArray *array = [_app.persistentContainer.viewContext executeFetchRequest:request error:nil];
    if (array.count) {
        for (Dog *dog in array) {
            dog.name = @"jerry8";
        }
        [_app saveContext];
        NSLog(@"UPDATA SUCCESS!!");
    } else
        NSLog(@"NOT FOUND DATA");
}
这段代码先根据给定条件查询到对应的数据组,再进行更改。

-(void)deleteCoreData{
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dog" inManagedObjectContext:_app.persistentContainer.viewContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.entity = entity;
    
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name=%@",@"tom0"];
    request.predicate = predicate;

    NSArray *array = [_app.persistentContainer.viewContext executeFetchRequest:request error:nil];
    if (array.count) {
        for (Dog *dog in array) {
            [_app.persistentContainer.viewContext deleteObject:dog];
        }
        NSLog(@"DELETE SUCCESS!!");
    } else
        NSLog(@"NOT FOUND DATA");
}

最重要的就是调用上下文的deleteObject方法

你可能感兴趣的:(CoreData)