核心数据持久化基础指南
立即解锁
发布时间: 2025-08-24 01:05:45 阅读量: 1 订阅数: 7 

### 核心数据持久化基础指南
#### 实体与托管对象
实体和托管对象这两个术语可能会让人有些混淆,因为它们都指代数据模型对象。实体是对对象的描述,而托管对象则是在运行时创建的该实体的实际具体实例。在数据模型编辑器中,我们创建实体;而在代码里,我们创建和检索托管对象。这就类似于类和类的实例之间的区别。
实体由属性组成,属性主要有以下三种类型:
1. **属性(Attributes)**:在 Core Data 实体中,属性的作用与 Objective - C 类中的实例变量相同,用于存储数据。
2. **关系(Relationships)**:关系定义了实体之间的联系。例如,创建一个 Person 实体时,我们可以先定义一些属性,如 hairColor、eyeColor、height 和 weight。对于地址信息,我们可以选择定义 state 和 ZIP code 等属性,也可以将这些地址信息嵌入到一个单独的 HomeAddress 实体中。假设采用后一种方式,那么就需要在 Person 和 HomeAddress 之间创建一个关系。关系可以分为一对一和一对多。通常,Person 到 HomeAddress 的关系是一对一的,因为大多数人只有一个家庭地址;而 HomeAddress 到 Person 的关系可能是一对多的,因为一个家庭地址可能居住着多个人。
3. **获取属性(Fetched properties)**:获取属性是关系的一种替代方式。它允许我们创建一个查询,在获取数据时进行评估,以确定哪些对象属于该关系。继续以 Person 实体为例,一个 Person 对象可以有一个名为 Neighbors 的获取属性,用于查找数据存储中与该 Person 的 HomeAddress 具有相同 ZIP 代码的所有 HomeAddress 对象。由于获取属性的构建和使用方式,它们始终是单向关系,并且是唯一一种可以跨多个数据存储进行遍历的关系。
通常,属性、关系和获取属性是使用 Xcode 的数据模型编辑器来定义的。
#### 键值编码
在代码中,我们使用键值编码来设置属性或检索其现有值,而不是使用访问器和修改器。键值编码听起来可能有些复杂,但其实我们在很多地方都已经使用过它,比如每次使用 NSDictionary 时,就是在使用键值编码,因为字典中的每个对象都存储在一个唯一的键值下。Core Data 使用的键值编码比 NSDictionary 中的稍微复杂一些,但基本概念是相同的。
当处理托管对象时,用于设置或检索属性值的键就是我们要设置的属性的名称。以下是从托管对象中检索名为 name 的属性值的示例代码:
```objc
NSString *name = [myManagedObject valueForKey:@"name"];
```
同样,设置托管对象属性的新值的代码如下:
```objc
[myManagedObject setValue:@"Gregor Overlander" forKey:@"name"];
```
#### 托管对象的存储环境
托管对象存储在持久存储(Persistent store)中,也称为后备存储。持久存储有多种形式,默认情况下,Core Data 应用程序将后备存储实现为存储在应用程序 Documents 目录中的 SQLite 数据库。尽管数据是通过 SQLite 存储的,但 Core Data 框架中的类会处理与加载和保存数据相关的所有工作。使用 Core Data 时,我们无需编写任何 SQL 语句,只需处理对象,Core Data 会在幕后完成必要的操作。
SQLite 并不是 Core Data 唯一的存储选项,后备存储还可以实现为二进制平面文件,甚至以 XML 格式存储。另外,还可以创建内存存储,这在编写缓存机制时可能会用到,但它不会在当前会话结束后保存数据。在大多数情况下,建议使用默认的 SQLite 作为持久存储。
虽然大多数应用程序只有一个持久存储,但在同一个应用程序中也可以有多个持久存储。如果想了解后备存储的创建和配置方式,可以查看 Xcode 项目中的 BIDAppDelegate.m 文件。我们选择的 Xcode 项目模板为应用程序设置单个持久存储提供了所需的所有代码。
通常,我们不会直接与持久存储进行交互,而是使用托管对象上下文(Managed object context),通常简称为上下文。上下文管理对持久存储的访问,并维护自上次保存对象以来哪些属性发生了变化的信息。上下文还会将所有更改注册到撤销管理器中,这意味着我们可以撤销单个更改,或者回滚到上次保存数据的状态。
需要注意的是,虽然可以有多个上下文指向同一个持久存储,但大多数 iOS 应用程序只使用一个上下文。
#### 托管对象的创建与检索
1. **创建新的托管对象**:创建托管对象的新实例比使用 alloc 和 init 创建普通对象实例稍微复杂一些。我们使用 NSEntityDescription 类中的 insertNewObjectForEntityForName:inManagedObjectContext: 工厂方法。NSEntityDescription 的作用是跟踪应用程序数据模型中定义的所有实体。该方法返回一个在内存中表示单个实体的实例。它返回的可能是一个 NSManagedObject 实例,该实例已设置了该特定实体的正确属性;或者,如果我们将实体配置为使用 NSManagedObject 的特定子类实现,则返回该子类的实例。示例代码如下:
```objc
theLine = [NSEntityDescription
insertNewObjectForEntityForName:@"EntityName"
inManagedObjectContext:context];
```
调用该方法后,对象存在于上下文中,但尚未成为持久存储的一部分。只有在调用托管对象上下文的 save: 方法时,对象才会被添加到持久存储中。
2. **检索托管对象**:要从持久存储中检索托管对象,我们需要使用获取请求(Fetch request),这是 Core Data 处理预定义查询的方式。例如,我们可以发出“给我所有眼睛颜色为蓝色的 Person 对象”这样的查询。
创建获取请求的步骤如下:
- 首先创建一个获取请求实例:
```objc
NSFetchRequest *request = [[NSFetchRequest alloc] init];
```
- 然后提供一个 NSEntityDescription,指定要检索的对象的实体:
```objc
NSEntityDescription *entityDescr = [NSEntityDescription
entityForName:@"EntityName" inManagedObjectContext:context];
[request setEntity:entityDescr];
```
- 我们还可以使用 NSPredicate 类为获取请求指定条件。谓词类似于 SQL 中的 WHERE 子句,允许我们定义用于确定获取请求结果的条件。以下是一个简单的谓词示例:
```objc
NSPredicate *pred = [NSPredicate predicateWithFormat:@"(name = %@)", nameString];
[request setPredicate: pred];
```
- 最后,使用 NSManagedObjectContext 的实例方法执行获取请求:
```objc
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil) {
// handle error
}
```
执行 executeFetchRequest:error: 方法后,它会从持久存储中加载指定的对象并以数组形式返回。如果遇到错误,将返回 nil 数组,并且提供的错误指针将指向一个描述具体问题的 NSError 对象。否则,将返回一个有效的数组,不过该数组可能不包含任何对象,因为可能没有对象满足指定的条件。从这一点开始,对返回数组中的托管对象所做的任何更改都将由执行请求的托管对象上下文跟踪,并在向该上下文发送 save: 消息时保存。
以下是创建和检索托管对象的流程 mermaid 图:
```mermaid
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(创建新托管对象):::process
B --> C{对象是否创建成功}:::decision
C -- 是 --> D(对象存在于上下文中):::process
D --> E(调用上下文 save: 方法):::process
E --> F(对象添加到持久存储):::process
C -- 否 --> G(处理创建错误):::process
A --> H(检索托管对象):::process
H --> I(创建获取请求):::process
I --> J(指定实体描述):::process
J --> K{是否指定谓词条件}:::decision
K -- 是 --> L(设置谓词条件):::process
K -- 否 --> M(执行获取请求):::process
L --> M
M --> N{请求是否成功}:::decision
N -- 是 --> O(返回对象数组):::process
N -- 否 --> P(处理请求错误):::process
O --> Q(对对象进行操作):::process
Q --> R(上下文跟踪更改):::process
R --> S(调用上下文 save: 方法保存更改):::process
```
#### 设计数据模型
1. 打开 Xcode 的数据模型编辑器,选择 Core_Data_Persistence.xcdatamodel。数据模型编辑面板会显示数据模型中包含的所有实体、获取请求和配置。配置可以定义数据模型中实体的一个或多个命名子集,在某些情况下很有用,但这里暂不详细讨论。
2. 点击实体面板左下角标有 Add Entity 的加号图标,创建一个名为 Entity 的新实体。
3. 在编辑区域右下角的控制按钮处,可以在表格视图(Table view)和图形视图(Graph view)之间切换。图形视图以图形方式展示实体及其属性和关系,对于包含多个实体的模型很有用;而表格视图在创建新实体时能显示更多细节,通常更实用。这里建议先切换到图形视图查看,然后切换回表格视图进行后续操作。
4. 按下 Style 菜单快捷键打开 Core Data 数据模型检查器,将实体的名称从 Entity 更改为 Line。
5. 在表格视图中,选中 Line 实体,点击编辑区域右下角的加号图标。如果点击并按住鼠标按钮,会
0
0
复制全文
相关推荐










