Core Data - 数据持久化

前言

广而告之 这里有其他人对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,这里需要注意,你所创建的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 - 数据持久化)