美文网首页
Core Data - 数据持久化

Core Data - 数据持久化

作者: 我的名字好长好长灬 | 来源:发表于2020-03-13 19:03 被阅读0次

    前言

    广而告之 这里有其他人对CoreData的理解,下边是对部分内容进行的引用。

    CoreData是Apple官方为iOS提供的一个数据持久化方案,其本质是一个通过封装底层数据操作,让程序员以面向对象的方式存储和管理数据的ORM框架(Object-Relational Mapping:对象-关系映射,简称ORM)。虽然底层支持SQLite、二进制数据、xml等多种文件存储,但是主要还是用来操作SQLite数据库。

    这里不对上文进行评价,接下来介绍对Core Data的理解。

    嗯,说一句吧,Core data 是Object-oriented database。

    Core Data

    Manage object graphs and object lifecycle, including persistence.

    不管是面向对象也好,面向过程也好,最终处理的还是数据,所以,Core Data 也就是为了解决我们抽象出来的对象和数据之间的关系。

    使用Core Data 第一个好处就是 它可以大大减少我们的工作量 不需要懂SQL ,就像我们使用xib、storyboard一样,简单的事情做多了就没有意思了,至于懂不懂SQL,因人而异,我觉得把自己那些东西学明白了就好了,不然,今天别人给你安利一个这,看个这,明天弄个那,没意思,过几天回过头,看一眼自己的工程,一团糟,苹果一天不倒闭,你就有饭吃,别焦虑。

    1创建Core Data、 存储和读取

    如果创建工程的时候☑️勾选了Core Data。

    如果忘记了勾选,可以创建一个,创建时候需要注意,创建的是Data Model,不是Mapping Model。

    创建成功后,项目中会出现 .xcdatamodeld 文件,而且还会在AppDelegate.swift中出现下边这些代码。

    funcapplicationWillTerminate(_application:UIApplication) {

            // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

            // Saves changes in the application's managed object context before the application terminates.

            self.saveContext()

        }

        // MARK: - Core Data stack

        lazyvarpersistentContainer:NSPersistentContainer= {

            /*

             The persistent container for the application. This implementation

             creates and returns a container, having loaded the store for the

             application to it. This property is optional since there are legitimate

             error conditions that could cause the creation of the store to fail.

            */

            letcontainer =NSPersistentContainer(name:"codedata")

            container.loadPersistentStores(completionHandler: { (storeDescription, error)in

                ifleterror = errorasNSError? {

                    // Replace this implementation with code to handle the error appropriately.

                    // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                    /*

                     Typical reasons for an error here include:

                     * The parent directory does not exist, cannot be created, or disallows writing.

                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.

                     * The device is out of space.

                     * The store could not be migrated to the current model version.

                     Check the error message to determine what the actual problem was.

                     */

                    fatalError("Unresolved error \(error), \(error.userInfo)")

                }

            })

            returncontainer

        }()

        // MARK: - Core Data Saving support

        funcsaveContext () {

            letcontext =persistentContainer.viewContext

            ifcontext.hasChanges{

                do{

                    trycontext.save()

                }catch{

                    // Replace this implementation with code to handle the error appropriately.

                    // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                    letnserror = errorasNSError

                    fatalError("Unresolved error \(nserror), \(nserror.userInfo)")

                }

            }

        }

    如果是New File创建的,那么你就需要这些代码,需要将NSPersistentContainer中的name更改为你所New的File name。如果你程序访问数据库,或怎么怎么地,出现问题,哎,这样的事情,试着在applicationWillTerminate中调用一下saceContext()方法。

    至于为什么调用该方法呢?

    保存你的数据库,到disk中,disk是啥?嗯。


    如果你不想使用 NSPersistenContainer 方法对数据库进行保存,或者你想将数据库保存到其他位置,可以尝试使用  NSManagedObjectModel + NSPersistentStoreCoordinator 的形式进行存储操作。

    guard let modelURL = Bundle.main.url(forResource:"DataModel", withExtension:"momd")else{

        fatalError("failed to find data model")

    }

    guard let mom = NSManagedObjectModel(contentsOf: url)else{

        fatalError("Failed to create model from file:\(url)")

    }

    先拿出你的model 然后 通过 NSPersistentStoreCoordinator 保存

    let psc =NSPersistentStoreCoordinator(managedObjectModel: mom)

    let dirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last     

     let fileURL = URL(string: "DataModel.sql", relativeTo: dirURL)

    do {

        try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: fileURL, options: nil)

    } catch {

        fatalError("Error configuring persistent store: \(error)")

    }

    最后还需要这,你存储/读取的大部分时间都是和 NSManagerObjectContext 打交道的

     let context = persistentContainer.viewContext

    let moc =NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)             

    moc.persistentStoreCoordinator = psc

    经验之谈

    这里可以写 static 方法,调用起来也方便,多个数据库,就创建多个(但不遵循我不写重复代码的原则呀!!)。

    static var persistentContainer:NSPersistentContainer{

               return (UIApplication.shared.delegate as! AppDelegate).persistentContainer

    }

    static var coreDataViewContext:NSManagedObjectContext {  

             return AppDelegate.persistentContainer.viewContext 

    }

    2 使用Core Data - ugly

    使用core data 存储的时候 需要借助 刚刚那个viewContext,just like this->

    let context = AppDelegate.coreDataViewContext

    letpeoperObject:NSManagedObject=NSEntityDescription.insertNewObject(forEntityName:"People", into: context)

    peoperObject.setValue("zhanshan", forKey:"name")

    读取一样的道理

    peoperObject.value(forKey:"name")

    what? 这NM还不如不用呢! (一看见别人写key-value,我就不舒服)

    3 使用Core Data  - 存储和读取

    可以通过修改xcdatamodeld文件中ENTITLES中的Codegen来减少工作量。

    1修改Codegen - 为Category/Extension。

    2新建一个ENTITLES 继承NSManagedObject

    然后你可以尝试自己new一个你新建的对象,你会发现,你没有写任何属性,它就存在你core data 中对应ENTITIES的属性。

    其实当你新建的时候,core data 就给你已经写好了。你创建的ENTITIES 为people 那么它就会给你写好extension,有什么Attributes,就同样创建对应的属性供你使用。

    //extension People {

    //

    //    @nonobjc public class func fetchRequest() -> NSFetchRequest {

    //        return NSFetchRequest(entityName: "People")

    //    }

    //

    //    @NSManaged public var age: Int16

    //    @NSManaged public var birthday: Date?

    //    @NSManaged public var create: Date?

    //    @NSManaged public var name: String?

    //    @NSManaged public var sex: String?

    //    @NSManaged public var adopt: NSSet?

    //

    //}

    如果ENTITIES之间存在relationship,那么ENTITIES即存在对应关系(1对1,1对多),例如:people和dog存在relationship,且关系为To Many,那么你通过查询 得到了具体某个people时,操作dog需要调用people.managedObjectContext来对dog进行操作。

    其中ENTITIES实例化需要 NSManagedObjectContext

      @available(iOS 10.0, *)

        public convenience init(context moc: NSManagedObjectContext)

    通过重写 ENTITIES 中 NSManagedObjectContext 的方法来实现一些操作,比如 prepareForDeletion,ENTITIES在被删除的时候会调用这个方法。

    3 使用Core Data  - 查询

    查询某个ENTITIES下的数据需要使用对应ENTITIES下的 fetchRequest() , 例如 People.fetchRequest().

    上一步返回NSFetchRequest。

    通过配置request的predicate,来进行匹配查询,多个匹配规则使用NSCompoundPredicate,NSPredicate在使用的时候需要注意。

    通过配置sortDescriptors,来对数据结果进行排序,可以使用多个sortDescriptor。NSSortDescriptor使用的时候需要注意,如果使用自定义排序算法,需要给对应属性写扩展方法,例如,NSNumber的属性,就对应写好它的扩展排序方法,并通过返回NSComparisonResult(其中包含返回结果)来进行排序。

    通过context对象调用fatch 进行查询,例如let info =try? context.fetch(request)。

    查询返回结果 NSFetchRequest<NSFetchRequestResult>,这里需要注意,你所创建的ENTITIES都默认遵守NSManagedObject : NSFetchRequestResult协议 ,所以对于返回结果可以如下。

        for item in result ?? [] {

                let p:People = item

                print(p.name??"ss")

            }

    应对面试,到这里就可以了。

    如果你是工作中使用,那么接下来就是经验之谈了。

    网络请求数据本地化

    是否存在一种不需要赋值,数据主动映射的方法,这样我们可以直接写网络请求,然后什么也不用管,数据就直接映射到model里,还进行了本地化?用的时候直接从Core Data取?

    是的,有

    基本上的流程就是 Data ->JSONModel ->NSManagedObject-> Core Data.

    你需要将Core Data 中的ENTITIES,relationship使用的非常熟练,是非常熟练。

    如果上一步做的没有问题,那么你工程中会出现好多没有属性和方法的NSManagedObject,你的Core Data 界面也非常有条理,ENTITIES之间关联的线也连好了。

    下一步 

    是否存在JSONModel-NSManagedObject有一种关联,那么问题基本就解决了。

    是的,有

    github有一个库Groot,真好。

    Groot provides a simple way of serializing Core Data object graphs from or into JSON.

    It uses annotations in the Core Data model to perform the serialization and provides the following features:

    Attribute and relationship mapping to JSON key paths.

    Value transformation using named NSValueTransformer objects.

    Object graph preservation.

    Support for entity inheritance

    简单介绍一下,这个库能够完成JSONModel-NSManagedObject互相转换,😄。

    下载完库,你会发现,百度里好像没有几个人对这个库进行解释。

    有两种人,

    1.如果你是按照(AppDelegate.persistentContainer.viewContext ),那么你就不需要去了解它的方法介绍。

    2.如果你是按照(let moc =NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) ),那么你可能会做很多无用功,你需要读代码特别熟练,你去下载源码Demo多看看也就会了。

    不用问,我肯定用的是第一种。

    如果你拿到了Data数据,可以直接调用 objects(fromJSONData: data, inContext:NSManagedObjectContext).它就可以给你返回。

    如果你处理的JSONObject,那么你需要使用它的另外两个方法。

    好了,👌。

    你会发现能正常存储,读取,但是ENTITIES中的数据都是nil。

    接下来你就需要注意了,你需要到Core Data 界面配置JSONKeyPath。

    每一个Attributes都配置,注意 ,是添加Attributes中User Info ,“+” 加上JSONKeyPath,value为你网络请求数据中的key。

    每一个relationship都需要配置,配置原则和Attributes一样。

    非常完美,不用垒代码真好。

    最后,

    try context.save()

    我希望你在操作过Core Data数据后,能够优美的调用save方法。

    相关文章

      网友评论

          本文标题:Core Data - 数据持久化

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