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<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方法。

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

推荐阅读更多精彩内容