Core Data 概述

article/2025/8/19 20:25:55

Core Data 概述

转载自: http://www.cocoachina.com/newbie/basic/2013/0911/6981.html


Core Data可能是OS X和iOS里面最容易被误解的框架之一了,为了帮助大家理解,我们将快速的研究Core Data,让大家对它有一个初步的了解,对于想要正确使用Core Data的同学来说,理解它的概念是非常必要的。几乎所有对Core Data感到失望的原因都是因为对它工作机制的错误理解。让我们开始吧。

 

 

Core Data是什么?

大概八年前,2005的四月份,Apple发布了OSX10.4,正是在这个版本中Core Data框架发布了。那个时候YouTube也刚发布。

 

Core Data是一个模型层的技术。Core Data帮助你建立代表程序状态的模型层。Core Data也是一种持久化技术,它能将模型对象的状态持久化到磁盘,但它最重要的特点是:Core Data不仅是一个加载、保存数据的框架,它还能和内存中的数据很好的共事。

 

如果你之前曾经接触过Object-relationalmaping(O/RM):Core Data不仅是一种O/RM。如果你之前曾经接触过SQL wrappers:Core Data不是一个SQL wrapper。它默认使用SQL,但是,它是一种更高级的抽象概念。如果你需要的是一个O/RM或者SQL wrapper,那么Core Data并不适合你。

 

对象图形管理是CoreData提供的最强大的功能之一。为了更好利用Core Data,这是你需要理解的一块内容。

 

还有一点要注意:CoreData是完全独立于任何UI层级的框架。它是作为模型层框架被设计出来的。在OS X中,甚至在一些后台驻留程序中,Core Data都起着非常重要的意义。

 

堆栈

Core Data有相当多可用的组件。这是一个非常灵活的技术。在大多数的使用情况下,设置都是相当简单。

 

当所有的组件都捆绑到一起的时候,我们把它称作Core Data堆栈,这个堆栈有两个主要部分。一部分是关于对象图形管理,这正是你需要很好掌握的那一部分,并且知道怎么使用。第二部分是关于持久化,比如,保存你模型对象的状态,然后再恢复模型对象的状态。

 

在两个部分之间,即堆栈中间,是持久化存储协调器(PSC)。它将对象图形管理部分和持久化部分捆绑在一起,当它们两者中的任何一部分需要和另一部分交流时,这便需要PSC来调节了。

 

对象图形管理是你程序模型层的逻辑存在的地方。模型层的对象存在于一个context内。在大多数的设置中,存在一个context,并且所有的对象存在于那个context中。Core Data支持许多contexts,但是,对于更多高级的使用情况,每个context和其他context区分的都很清楚。需要记住的是,对象和他们的context是相关联的,每个被管理的对象都知道自己属于哪个context,并且每个context都知道自己管理着哪个对象。

 

堆栈的另一部分就是持久化发生的地方了,即是Core Data从文件系统读或写的地方。持久化存存储协调器(persistent store coordinator)都有一个属于自己的持久化存储,并且这个store在文件系统中与SQLite数据库交互。为了支持更高级的设置,Core Data可以将多个存储附属于同一个持久化存储协调器,并且除了SQL可选择的类型外,还有很多存储类型可供选择。

 

最常见的解决方案,如下图所示:


 

组件如何一起工作

 让我们快速的看一个说明组件如何协同工作的例子。在我们a full application using Core Data的文章中,我们正好有一个实体,即一种对象:我们有一个Item 实体对应一个title。每一个item可以拥有子items,因此,我们有一个父子关系。

 

这是我们的数据模型,正如我们在Data Models and Model Objects文章中提到的一样,在CoreData中一种特别的对象——实体。在这种情况下,我们只有一个实体:Item实体。同样的,我们有一个NSManagedObject的子类,叫做Item。这个Item实体映射到Item类上。在data models article中会详细的谈到这个。

 

我们的程序仅有一个根Item。这并没有什么奇妙的地方。这是一个我们用来显示底层item等级的Item。这是一个我们永远不会为其设置父类的Item。

 

当程序运行时,我们像上面图片描绘的一样设置我们的堆栈,一个存储,一个managed object context,一个持久化存储协调器来将他们两个关联。

 

在第一次运行时,我们并没有任何items。我们需要做的第一件事就是创建根item。你通过将他们插入context来增加管理对象。

 

创建对象

 

插入对象的方法似乎很笨重,我们通过NSEntityDescription的方法来插入:

 
  1. + (id)insertNewObjectForEntityForName:(NSString *)entityName  
  2.                inManagedObjectContext:(NSManagedObjectContext *)context 

 

我们建议你增加两个方便的方法到你的模型类中:

 
  1. + (NSString *)entityName 
  2.    return @“Item”; 
  3.  
  4. + (instancetype)insertNewObjectInManagedObjectContext:(NSManagedObjectContext *)moc; 
  5.    return [NSEntityDescription insertNewObjectForEntityForName:[self entityName]  
  6.                                         inManagedObjectContext:moc]; 

 

现在,我们可以像这样插入我们的根对象了:

 

 
  1. Item *rootItem = [IteminsertNewObjectInManagedObjectContext:managedObjectContext]; 

 

现在,在我们的managedobject context(MOC)中有一个唯一的item。Context知道这是一个新插入进来需要被管理的对象,并且被管理的对象rootItem知道这个Context(它有一个-managedObjectContext方法)。

 

保存改变

这时候,可是我们还是没有接触到持久化存储协调器或持久化存储。新的模型对象—rootItem,仅仅在内存中。如果我们想要保存模型对象的状态(在这种情况下只是一个对象),我们需要保存context:

 
  1. NSError *error = nil; 
  2.  
  3. if (! [managedObjectContext save:&error]) {  // Uh, oh. An error happened. :( )} 

 

这个时候,很多事情将要发生。首先发生的是managed object context计算出改变的内容。这是context的职责,追踪出任何你在context管理对象中做出的改变。在我们的例子中,我们到现在做出的唯一改变就是插入一个对象,即我们的rootItem。

 

然后Managed object context将这些改变传给持久化存储协调器,让它将这些改变传播给store。持久化存储协调器会协调store来将我们插入的对象写入到磁盘上的SQL数据库。NSPersistentStore类管理着和SQLite的实际交互,并且产生需要被执行的SQL代码。持久化存储协调器的规则就是简单的调整store和context之间的交互。在我们的例子中,这个规则相当简单,但是,复杂的设置可以有多个stores和多个contexts。

 

更新关系

Core Data的能力在于管理关系。让我们着眼于简单的情况—增加我们第二个item,并且使它成为rootItem的子item:

 
  1. Item *item = [IteminsertNewObjectInManagedObjectContext:managedObjectContext]; item.parent =rootItem; item.title = @"foo"

 

好了。这些改变再次的仅仅存在于managed object context中。一旦我们保存了context, managed object context将会告诉持久化存储协调器,像增加第一个对象一样增加新创建的对象到数据库文件中。但这也将会更新从第二个item到第一个item之间的关系,或从第一个到第二个item的关系。记住Item实体是如何有一个父子关系的。同时他们之间有相反的关系。因为我们设置第一个item为第二个item的父类时,第二个item将会变成第一个item的子类。managed object context追踪这些关系,持久化存储协调器和store保存这些关系到磁盘。

 

弄清对象

我们已经使用我们程序一会儿了,并且已经为根item增加了一些sub-items,甚至sub-items到sub-items。然后,我们再次启动我们的程序。Core Data已经将这些items之间的关系保存到了数据库文件。对象图形是持久化的。我们现在需要取出根item,所以我们可以显示底层items的列表。有两种方法。我们先看简单点的方法。

 

当根Item对象创建并保存之后我们可以向它请求它的NSManagedObjectID,这是一个不透明的对象,可以唯一代表该根Item。我们可以保存这个对象到NSUSerDefaults,像这样:

 
  1. NSUserDefaults *defaults = [NSUserDefaultsstandardUserDefaults];                
  2. [defaultssetURL:rootItem.managedObjectID.URIRepresentationforKey:@"rootItem"]; 

 

现在,当程序重新运行时,我们可以像这样返回得到这个对象:

 

 
  1. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
  2. NSURL *uri = [defaults URLForKey:@"rootItem"]; 
  3. NSManagedObjectID *moid = [managedObjectContext.persistentStoreCoordinator managedObjectIDForURIRepresentation:uri]; 
  4. NSError *error = nil; 
  5. Item *rootItem = (id) [managedObjectContext existingObjectWithID:moid error:&error]; 

 

很明显,在一个真实的程序中,我们需要检查NSUserDefaults是否真正返回一个有效值。

 

managed object context要求持久化存储协调器从数据库取得指定的对象。根对象现在被恢复到context中。然而,其他所有的items仍然不在内存中。

 

rootItem有一个子关系。但那儿还没有什么。我们想要显示rootItem的子item,因此我们需要调用:

 
  1. NSOrderedSet *children = rootItem.children; 

 

现在发生的是,context标注这个根item的子关系为默认。Core Data已经标注这个关系为仍需要被解决。既然我们已经在这个时候访问了它,context将会自动配合持久化存储协调器来将这些子 items载入到context中。

 

这听起来可能非常不重要,但是在这个时候真正发生了很多事情。如果任何子对象偶然发生在内存中,Core Data保证会复用那些对象。这是Core Data独一无二的功能。在context内,从不会存在第二个相同的单一对象来代表一个给定的item。

 

第二,持久化存储协调器有它自己内部对象值的缓存。如果context需要一个指定的对象(比如一个子item),并且持久化存储协调器在缓存中已经有需要的值,那么,对象可以不通过store而被直接加到context。这很重要,因为访问store就意味这执行SQL代码,这比使用内存中存在的值要慢很多。

      

随着我们越过item到子item到子item的子item,我们慢慢地把整个对象图形引用到了managed object context。而这些对象都在内存中之后,操作对象以及传递关系就会变得非常快,既然我们只是在managed object context里操作。我们跟本不需要访问持久化存储协调器。在我们的Item对象上访问title,父子属性是非常快而且高效的。

 

由于它会影响性能,所以了解数据在这些情况下怎么取出来是非常重要的。在我们特定的情况下,由于我们并没接触到太多的数据,所以这并不算什么,但是一旦你接触了,你将需要了解在背后发生了什么。

 

当你越过一个关系(比如在我们例子中的父关系或子关系)下面三种情况将有一种会发生:

 

(1)对象已经在context中,这种操作基本上是没有任何代价的。

 

(2)对象不在context中,但是因为你最近从store中取出过对象,所以持久化存储协调器缓存了对象的值,这个操作还算廉价(但是,一些操作会被锁住)。

 

操作耗费最昂贵的情况是(3),当context和持久化存储协调器都是第一次访问这个对象,这中情况必须通过store从SQLite数据库取回。第三种情况比(1)和(2)需要付出更多代价。

 

如果你知道你必须从store取回对象(你没有这些对象),当你限制一次取回多少个对象时,将会产生很大的不同。在我们的例子中,我们希望一次性取出所有child items,而不是一个接一个。我们可以通过一个特别的技巧NSFetchRequest,但是我们要注意,当我们需要做这个操作时,我们只需要执行一次取出请求,因为一次取出请求将会造成选项(3)发生;这将总是访问SQLite数据库。

 

因此,当需要显著提升性能时,检查对象是否已经存在将变得非常有意义。你可以使用-[NSManagedObjectContext objectRegisteredForID:]来检测一个对象是否已经存在。

 

改变对象的值

现在,我们可以说,我们已经改变我们一个Item对象的title:

 
  1. item.title = @"New title";  

 

当我们这样做时,items的title改变了,此外,managed object context会标注这个item已经被改变,这样当我们在context中调用-save:时,这个对象将会通过持久化存储协调器和附属的store保存起来。context最关键的职责之一就是跟踪任何改变。

 

从最后一次保存开始,context知道哪些对象被插入,改变,删除。你可以通过-insertedObjects, -updatedObjects, and –deletedObjects方法来执行这些操作。同样的,你可以通过-changedValues方法来询问一个被管理的对象哪些值被改变了。这个方法正是Core Data能够将你做出的改变推入到数据库的原因。

 

当我们插入一个新的Item对象时,Core Data知道需要将这些改变存入store。那么,将你改变对象的title时,也会发生同样的事情。

 

保存values需要协调持久化存储协调器和持久化store依次访问SQLite数据库。当和在内存中操作对象比起来,取出对象和值,访问store和数据库是非常耗费资源的。不管你保存了多少更改,一次保存的代价是固定的。并且每个变化都有成本,这仅仅是SQLite的工作。当你做很多更改的时候,需要将更改打包,并批量更改。如果你保存每一次更改,将要付出很高的代价,因为你需要经常做保存操作。如果你很少做保存,那么你将会有一大批更改交给SQLite处理。

 

同样需要注意到,保存操作是原子性的。他们都是事务。要么所有的更改会被提交给store/SQLite数据库,要么任何更改都不被保存。当实现自定义NSIncrementalStore基类时,这一点一定要牢记在心。要么确保保存永远不会失败(比如说不会发生冲突),要么当保存失败时,你store的基类需要恢复所有的改变。否则,在内存中的对象图形最终和保存在store中的对象不一致。

 

如果你使用一个简单的设置,保存操作通常不会失败。但是Core Data允许每个持久化存储协调器有多个context,所以你可能陷入持久化存储协调器层级的错误。改变是对于每个context的,另一个context的更改可能导致冲突。Core Data甚至允许完全不同的堆栈访问磁盘上相同的SQLite数据库。这明显也会导致冲突(比如,一个context想要更新一个对象的值,而另一个context想要删除这个对象)。另一个导致保存失败的原因可能是验证。Core Data支持对象复杂的验证政策。这个问题比较高级。一个简单的验证规则可能是:Item的标题不能超过300个字符。但是Core Data也支持通过属性进行复杂的验证政策。

 

结束语

 如果CoreData看起来让人害怕,这最有可能是因为它的灵活性允许你可以通过非常复杂的方法使用它。始终记住:尽量保持简单,它会让开发变得更容易,并且把你和你的用户从麻烦中拯救出来。除非你确信它会带来帮助,才去使用更复杂的东西,比如说是background contex。去使用一个简单的CoreData堆栈,并且使用我们在这篇文章中讲到的知识吧,你将很快会真正体会到Core Data能为你做什么,并且学到它是怎么缩短你开发周期的。



http://chatgpt.dhexx.cn/article/sSeHGEyE.shtml

相关文章

CoreData相关01 基础及运用:添加、查询、过滤、排序、分页

CoreData CoreData中几乎不用写数据库操作语句就能完成数据的本地化存储。 CoreData和iOS中的模型对象相联系在一起,只需要操作模型对象的增删改查就可以完成数据的增删改查,不用写SQL语句。 CoreData的存储方式 SQLite NSSQLiteStoreType XML NSXML…

JVM原理分析

JVM原理分析 为什么Java是一种跨平台语言 1、不同系统的JDK下载 JDK里面有:Jre和Jvm,其中就有JVM指令集的不同 JVM主要结构: 栈、堆、方法区、程序计数器、本地方法栈 mian方法的执行 通过类装载子系统进入JVM 其中栈中存放线程 来一个…

JVM实现原理

JVM原理 1、概念简介2、为什么要学习JVM虚拟机?3、JVM怎么学?3.1 内存管理1、jvm运行时数据区 1、概念简介 JVM:JAVA虚拟机JRE:Java运行环境 JREJVM虚拟机Java核心类库支持文件JDK:Java开发工具包 JDKJREJava工具Java…

【JVM调优】JVM原理与性能调优

一、参考资料 今日头条https://www.toutiao.com/i7007696978586976805

JVM原理和调优的理解和学习

JVM原理和调优的理解和学习 一、详解JVM内存模型二、JVM中一次完整的GC流程是怎样的三、GC垃圾回收的算法有哪些四、简单说说你了解的类加载器五、双亲委派机制是什么,有什么好处,怎么打破六、说说你JVM调优的几种主要的JVM参数七、JVM调优八、类加载的机…

JVM原理-jvm内存模型

一、jvm内存模型 结构图 JVM包含两个子系统和两个组件 Class loader(类装载子系统) 根据给定的全限定名类名(如:java.lang.Object)来装载class文件到 Runtime data area运行时数据区中的method area方法区中Execution engine(执行引擎) 执行classes中的指令Runt…

JVM原理-垃圾回收机制及算法

JVM原理-jvm内存模型 jvm内存模型 一、垃圾回收机制算法 1、 判断对象是否回收算法 垃圾收集器在做垃圾回收的时候,首先需要判定的就是哪些内存是需要被回收的,哪些对象是存活的,是不可以被回收的;哪些对象已经死掉了&#xf…

jvm面试原理

1、什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组…

JVM原理和JVM内存的整理

JVM原理及JVM内存 JVM原理及JVM内存概念这么说1.JVM的基本过程2.JVM的中的“解释”原理,三个重要机制3.JVM的体系结构4.运行时数据区JVM垃圾回收 JVM原理及JVM内存 之前看了许多JVM原理的文章、写作的大牛们都讲的很透彻,但是私下觉得:写得详…

JVM原理和调优(读这一篇就够了)

前言 抛2个问题: 1、export JAVA_OPTS"-Xms256m -Xmx512m -Xss256m -XX:PermSize512m -XX:MaxPermSize1024m -Djava.rmi.server.hostname136.64.45.24 -Dcom.sun.management.jmxremote.port9315 -Dcom.sun.management.jmxremote.sslfalse -Dcom.sun.manageme…

JVM工作原理

JVM的生命周期 (1)两个概念:JVM实例和JVM执行引擎实例 JVM实例对应了一个独立运行的java程序,它是进程级别的 JVM执行引擎实例则对应了属于用户运行程序的线程,它是线程级别的 (2)VM实例的诞生…

JVM原理与实战

JVM原理 类加载流程和内存分配栈帧操作一、JVM垃圾回收算法主动加载的几种方式?符号引用和直接引用1.1 什么是垃圾(Garbage Collection)回收?1.2 引用计数法(Reference Conting)1.3 标记清除法 *(Mark - Sweep)1.4 复制算法 *(Copying)1.4.1 复制算法在JVM中的应用1.…

jvm原理与性能调优

文章目录 一、JVM内存结构 1.运行时数据区 2.直接内存 二、JVM中的对象 1.对象的创建 2.对象的内存布局 3.对象的访问定位 三、垃圾回收算法和垃圾回收器 1. 如何判断对象是已死 2.分代回收理论 3.垃圾回收算法 4.垃圾收集器 四、JVM执行子系统 1.Class文件结构 2.类加载机制 3…

什么是JVM?深入解析JVM原理!

一、JVM是什么? JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在J…

全面阐述JVM原理

一 JVM入门 1. 什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码…

JVM原理

JVM原理 一.jvm简介 JVM是Java Virual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,他是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组…

JVM原理详解

主题内容: 1.JVM 内存区域概览 2.堆区的空间分配是怎么样?堆溢出的演示 3.创建一个新对象内存是怎么分配的? 4.方法区 到 Metaspace 元空间 5.栈帧是什么?栈帧里有什么?怎么理解? 6.本地方法栈 7.程序…

JVM的运行原理

目录 1.概念 2.JVM运行机制 3.JVM执行流程 4.JVM运行时数据区 什么是线程私有? OOM(内存溢出) 4.1 堆:也叫运行时数据区,线程共享 4.2 方法区:线程共享 4.3 Java虚拟机栈:线程私有,描…

JVM的原理

JVM原理(一) 1、JVM总体结构图 2、JVM堆结构图以及分代 复制算法: 1、新生成的对象存放在Eden区和from区 2、当Eden区内存不够,虚拟机将发起一次MinorGC 3、GC进行时,Eden区中所有存活的对象都会被复制到to区 4、年龄阀值达到15的会被放到老年…