美文网首页数据储存
iOS 数据存储(五) -持久化 Core Data

iOS 数据存储(五) -持久化 Core Data

作者: 搬砖的crystal | 来源:发表于2022-07-26 15:04 被阅读0次

    一、简介

    Core Data 是苹果官方提供的管理数据层对象的框架,它提供了对象—关系映射(ORM)的功能,即能够将 Objective-C 对象转化成数据,保存在 SQLite 数据库文件中,也能够将保存在数据库中的数据还原成 Objective-C 对象。在此数据操作期间,不需要编写任何 SQL 语句。Core Data 针对对象生命周期,以及持久化的对象图管理(object graph management)中的一些常见问题提供了解决方案。CoreData 的主要任务是负责数据更改的管理、序列化到磁盘、最小化内存占用以及查询数据。

    Core Data 比 SQLite 做了更进一步的封装,SQLite 提供了数据的存储模型,并提供了一系列 API,你可以通过 API 读写数据库,去处理想要处理的数据。但是 SQLite 存储的数据和你编写代码中的数据(比如一个类的对象)并没有内置的联系,必须你自己编写代码去一一对应。
    而 Core Data 却可以解决一个数据在持久化层和代码层的一一对应关系。也就是说,你处理一个对象的数据后,通过保存接口,它可以自动同步到持久化层里,而不需要你去实现额外的代码。

    iOS10 中利用 NSPersistentContainer
    iOS10 之前涉及 NSManagedObjectContextNSPersistentStoreCoordinatorNSManagedObjectModelNSPersistentStore 这些类。

    1.Core Data Stack

    说是一个对象的集合,由4个主要对象构成:

    • managed object context (NSManagedObjectContext)
    • persistent store coordinator (NSPersistentStoreCoordinator)
    • managed object model (NSManagedObjectModel)
    • persistent container (NSPersistentContainer)

    Core Data Stack,就是进行数据增删查改、保存的工作台,Apple 提供这样一个工作台,让你方便进行数据的保存。无需关心实现细节。

    2.Persistent Container

    NSPersistentContainer 是iOS 10、 macOS 10.12 之后才出现的新类。引入这个新类的目的之一,就是为了简化创建 Core Data Stack 这个工作台的过程。所以,在 iOS10 之前,创建 Core Data Stack 会复杂一些。

    而 Persistent Container 也有另一个新类 NSPersistentStoreDescription,可以利用这个类,进行一些定制化设置,比如自定义存储路径、设置存储数据方式等(Core Data 支持 SQLite、XML、Binary、InMemory 4中方式存储数据)。

    备注:iOS10中,如果利用 NSPersistentContainer 创建 Core Data Stack,预设的是 NSSQLiteStoreType 类型。并且默认打开了自动轻量化版本迁移功能(换言之,在 iOS10 之前,需要手动进行相关设置,才能打开版本迁移功能)。

    3.Managed Object Context。

    可以理解为是一块内存,提供了和 Managed Objects 交互的场所。也称为:The Context 或者 MOC。NSManagedObjectContext 类实例。
    备注:对数据进行删除、保存、查询,都要用到 NSManagedObjectContext 类的相关方法。

    4.Managed Object Model

    直观点,你可以把它理解为就是 Xcode 中后缀为 xcdatamodel 的文件。在这个文件里,你可以通过非代码、可视化的方式,定义对象、对象的属性、对象之间的关系(Core Data 把对象称呼为实体、对象的属性称呼为特性)。

    Managed Object Model 就是 Core Data 中用于描述实体、实体特性、实体间关系的一套方案。

    它是 NSManagenObjectModel 的类实例(也可以通过纯代码实现 .xcdatamodel文件的内容)。

    Entity - 实体

    NSEntityDescription 类实例,用于定义一个对象。一个实体,最少要有名字和类名(如果没有设置类名,默认是 NSManagedObject类)。

    Attribute - 特性

    实体特性 NSAttributeDescription 类实例。就是 Entity 的特性,对应 App 中的创建类时的属性。

    Relationship - 关系

    实体关系 NSRelationshipDescription 类实例。用于描述 Entity 之间的关系。

    5.Managed Object。

    就是需要保存的数据,是 NSManagenObject 类实例。(对应 App 中的对象)

    就我的理解,Managed Object 和上面提到的 Entity,本质上是同一个东西,就是你的数据对象,只不过是在可视化操作和纯代码操作中的不同称谓。

    6.Persistent Store Coordinator

    协调 Context 和 Persistent Store 的一个角色。NSPersistentStoreCoordinator 类实例。
    如果只是对数据进行简单的增删查改,我们并不需要接触到这个类。

    7.Persistent Store

    可以理解为保存数据的地方。用于设置保存数据的方式、以及保存的路径等。(保存数据的方式指 SQLite、XML、Binary、InMemory 4种)。NSPersistentStore 类实例。也称为 The Store 或者 Database。

    在 iOS10 之前,如果需要支持版本迁移功能,需要在创建 NSPersistentStore 类实例时,传入相应的 options 参数。而在 iOS10 中,则会自动打开版本迁移功能,并默认设置数据类型为 NSSQLiteStoreType

    版本迁移,假如修改了数据模型(比如修改了 . xcdatamodel 文件:增加了实体,增加了特性等等�),为了防止使用者在更新 App 后,由于数据模型不一致导致崩溃,需要进行一定的处理,这个处理叫版本迁移。

    二、使用

    1.初始化 Core Data Stack

    不过由于 iOS10 新引进了 NSPersistentContainer 类,然后新建项目又可以选择勾选 Core Data 与否。所以情况变得稍稍有点复杂。

    这里分三种情况:1、在既有项目(只需支持iOS10)初始化 Core Data Stack;2、在既有项目(需兼容iOS8、9、10等系统)初始化 Core Data Stack;3、新建项目时直接勾选了 Core Data。

    (1)在既有项目添加 Core Data 功能(只需支持iOS10)
    // 我们先声明了一个NSPersistentContainer类型的属性:persistentContainer,在适合的时间调用initWithName:对其初始化
    // 这里的Name参数,需要和后续创建的.xcdatamodeld模型文件名称一致。
    _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"Model"];
        
    // 调用loadPersistentStoresWithCompletionHandler:方法,完成Core Data Stack的最中初始化。
    // 如果不能初始化成功,在Block回调中打印错误,方便调试
    [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription * _Nonnull description, NSError * _Nullable error) {
            
        if (error != nil) {
            NSLog(@"Fail to load Core Data Stack : %@", error);
            abort();
        }
        else {
            ...
        }
    }];
    

    分为两步:

    • 初始化 NSPersistentContainer 对象
    • 调用 NSPersistentContainerloadPersistentStoresWithCompletionHandler: 完成初始化
    (2)在既有项目初始化 Core Data Stack(需兼容iOS8、9、10等系统)
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            NSInteger majorVersion = [NSProcessInfo processInfo].operatingSystemVersion.majorVersion;
            
            if (majorVersion < 10) {
                // iOS10以下的系统, 用旧有的方法初始化Core Data Stack
                [self initializeCoreDataLessThaniOS10];
            }
            else {
                // iOS10的系统, 用新的方法(详见上面介绍的情况1)
                [self initializeCoreData];
            }
        }
        return self;
    }
    
    
    - (void)initializeCoreDataLessThaniOS10 {
        // Get managed object model(拿到模型文件,也就是.xcdatamodeld文件(我们会在初始化完Core data Stack后创建))
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
        NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
        NSAssert(mom != nil, @"Error initalizing Managed Object Model");
        
        // Create persistent store coordinator(创建NSPersistentStoreCoordinator对象(需要传入上述创建的NSManagedObjectModel对象))
        NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
        
        // Creat managed object context(创建NSManagedObjectContext对象(_context是声明在.h文件的属性——因为其他类也要用到这个属性))
        _context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        
        // assgin persistent store coordinator(赋值persistentStoreCoordinator)
        _context.persistentStoreCoordinator = psc;
        
        // Create .sqlite file(在沙盒中创建.sqlite文件)
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
        NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];
        
        // Create persistent store(异步创建NSPersistentStore并add到NSPersistentStoreCoordinator对象中,作用是设置保存的数据类型(NSSQLiteStoreType)、保存路径、是否支持版本迁移等)
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
            // 用于支持版本迁移的参数
            NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                     [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
            NSError *error = nil;
            NSPersistentStoreCoordinator *psc = _context.persistentStoreCoordinator;
            
            // 备注,如果options参数传nil,表示不支持版本迁移
            NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType
                                                         configuration:nil
                                                                   URL:storeURL
                                                               options:options
                                                                 error:&error];
            NSAssert(store != nil, @"Error initializing PSC: %@\n%@", [error localizedDescription], [error userInfo]);
        });
    }
    
    (3)创建项目直接勾选 Use Core Data

    项目模版会在 AppDelegate 类中直接帮你初始化好 Core Data Stack。

    2.创建 managed object model

    选择 Core Data 栏目下的 Data Model ,就可以创建一个 .xcdatamodeld 模型文件。
    自己创建的默认系统提供的命名为 Model.xcdatamodeld
    添加实体、实体的特性、关系:

    (1)Entity

    当在 Xcode 中点击 Model.xcdatamodeld 时,会看到编辑视图 Add Entity


    如果把数据模型文件比作数据库中的“库”,那么 Entity 就相当于库里的实体。
    假设我这个数据模型是用来存放图书馆信息的,那么很自然的,我会想建立一个叫 Book 的 Entity。
    (2) Attributes

    当建立一个名为 Book 的 Entity 时,会看到视图中有栏写着 Attributes,我们知道,当我们定义一本书时,自然要定义书名,书的编码等信息。这部分信息叫 Attributes,即书的属性。


    (3) Relationship

    在我们使用 Entity 编辑时,除了看到了Attributes 一栏,还看到下面有 Relationships 一栏

    在 Reader 的 Relationship 下点击 + 号键。然后在Relationship 栏的名字上填 borrow,表示读者和书的关系是“借阅”,在 Destination 栏选择 Book,这样,读者和书籍的关系就确立了。
    对于第三栏,Inverse,需要创建 Book 的 Relationship 后在关联。

    选择 Book 的一栏,在 Relationship 下添加新的 borrowBy,Destination 是 Reader,这时候点击 Inverse 一栏,会发现弹出了 borrow,直接点上。

    那么在 Reader 的 Relationship 中,我们会发现 Inverse 一栏会自动补齐为 borrowBy。因为电脑这时候已经完全理解了双方的关系,自动做了补齐。

    一对一和一对多 - to one和to many

    我们建立 Reader 和 Book 之间的联系的时候,发现他们的联系逻辑之间还漏了一个环节。
    假设一本书被一个读者借走了,它就不能被另一个读者借走,而当一个读者借书时,却可以借很多本书。
    也就是说,一本书只能对应一个读者,而一个读者却可以对应多本书。
    这就是 一对一 → to one 和 一对多 → to many 。


    3.创建 NSManagedObject 子类
    (1)方法1

    直接 Command + N 创建一个新类,继承 NSManagedObject 类,然后定义的属性和模型文件中的一致。

    (2)方法2

    选中对应的实体,然后 Editor > Create NSManagedObject Subclass...,系统自动生成 NSManagedObject 子类。
    这种方法,如果有一对多的关系,会生成 2 个Category(Core Data生成的 NSManagedObject 子类,都是以 Category 形式存在的)
    CoreDataProperties:生成实体中 Attributes 对应的属性。Relationships 也会生成对应的属性:一对多关系是 NSSet/NSOrderSet 类型属性(本质是个集合),一对一关系则是非集合的对象类型属性。
    CoreDataGeneratedAccessors 其实就是一系列增加、删除 NSOrderSet/NSSet里元素的方法。(如果没有对多关系,不会有这个 Category)

    相关文章

      网友评论

        本文标题:iOS 数据存储(五) -持久化 Core Data

        本文链接:https://www.haomeiwen.com/subject/wqgmirtx.html