CoreData模板代码分析

2024-05-26 10:48
文章标签 分析 代码 模板 coredata

本文主要是介绍CoreData模板代码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前写过一篇CoreData的简单使用的文章,其Demo使用了Xcode中的CoreData模板(新建一个Empty模板的工程时,如果选择了Use Core Data选项,那么新建出来的工程便会附带上Core Data的代码)

但是之前并没有留意CoreData自动引入的模板代码的作用,最近又接触到了CoreData的内容,所以回头看了一下这部分代码。

首先看看Core Data的框架结构(下图来自Core Data Overview):



以下是我的对CoreData的初级认识:

1.在CoreData中有一个托管对象上下文(ManagedObjectContext),可以把它看做一个缓冲区,负责记录托管对象的状态和给出用户查询的对象等。

2.在上下文中存储的是各个托管对象(ManagedObject),也就是我们要持久化的对象数据,它们由托管对象模型(ManagedObjectModel)描述,其模型可以通过xcdatamodeld文件定制,xcdatamodeld文件在程序启动后加载到Bundle中并被解释成momd文件夹中的内容。

3.有多种方式来持久化对象数据,如SQLite数据库、XML文件、二进制文件等,这需要和底层的SQLite及File System打交道,因此要用到大量的SQL语句,使程序开发难度加大。幸好CoreData为我们给出了解决方案:持久化仓库统筹者(PersistentStoreCoordinator),CoreData将与底层的数据库和文件系统抽象成一个持久化仓库(PersistentStore),可以使用持久化仓库统筹者对各个仓库进行管理和操作,开发者并不需要关注数据的存储方式和操作查询等具体过程,此时持久化仓库统筹者就像一个ORM的角色,所以不要把CoreData简单地理解成一个ORM的框架。



要使用CoreData的相关类,首先要在项目中添加CoreData.framework并导入相关的头文件,在Use Core Data的项目中已经在pch预编译头文件中导入:

#import <Availability.h>#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif#ifdef __OBJC__#import <UIKit/UIKit.h>#import <Foundation/Foundation.h>#import <CoreData/CoreData.h>
#endif


下面是AppDelegate.h中自动生成的代码:

// managedObjectContext,managedObjectModel,persistentStoreCoordinator全部是一次性初始化的// 托管对象上下文,其中包含多个托管对象
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;// 托管对象模型:用来描述托管对象
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;// 持久化仓库统筹者:用来协调托管对象的存储过程和具体存储方式
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;// 将托管对象上下文写入到持久化仓库中
- (void)saveContext;// 获取程序Documents目录
- (NSURL *)applicationDocumentsDirectory;

AppDelegate.m文件内容见下文。

首先是AppDelegate中的几个方法,只有applicationWillTerminate方法中有和CoreData相关的代码:

- (void)applicationWillTerminate:(UIApplication *)application {// Saves changes in the application's managed object context before the application terminates.[self saveContext];
}
在程序将要终止时,要调用self的saveContext方法保存托管对象上下文。


saveContext方法实现如下:

- (void)saveContext {NSError *error = nil;NSManagedObjectContext *managedObjectContext = self.managedObjectContext;if (managedObjectContext != nil) {// 在上下文发送了变化的情况下才保存上下文,如果保存出错就报错并终止程序运行if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {// Replace this implementation with code to handle the error appropriately.// abort() 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. NSLog(@"Unresolved error %@, %@", error, [error userInfo]);abort();}}
}
注意

managedObjectContext hasChanges] && ![managedObjectContext save:&error]
这一行表明,只有上下文发生了变化才执行后面的save方法。如果save方法返回NO,那么保存失败,应该实现出错处理的代码。


然后是托管对象上下文的getter方法:

- (NSManagedObjectContext *)managedObjectContext {// lazy initialization的getter方法// 此时在类中访问属性时,应该使用getter方法来获取(self.xx),不要直接使用实例变量(_xx)if (_managedObjectContext != nil) {return _managedObjectContext;}NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];if (coordinator != nil) {// 初始化托管对象上下文,并将其与持久化仓库统筹者相关联_managedObjectContext = [[NSManagedObjectContext alloc] init];[_managedObjectContext setPersistentStoreCoordinator:coordinator];}return _managedObjectContext;
}

这里getter方法使用了lazy initialization的方式初始化,也就是一次性的初始化,因此在AppDelegate.m中,不能通过实例变量(即_managedObjectContext)的方式来获取,只能通过getter方法(self.managedObjectContext或[self managedObjectContext])来获取,否则该属性可能没有被初始化。

在新建一个托管对象上下文对象后,还要将其和一个持久化仓库统筹者相关联,实际上统筹者已经和各个持久化仓库相关联,这里就搭起了上下文到各个仓库之间的桥梁。


然后是持久化仓库统筹者的getter方法:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {// lazy initialization的getter方法if (_persistentStoreCoordinator != nil) {return _persistentStoreCoordinator;}// App的当前路径 / Documents / AppName.sqliteNSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataRef.sqlite"];NSError *error = nil;// 通过托管对象模型来初始化持久化仓库统筹者_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];// 根据配置选项将storeURL指定的存储介质(sqlite,XML,二进制文件,内存等)添加到统筹者的管辖范围内if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreTypeconfiguration:nilURL:storeURLoptions:nilerror:&error]){/*Replace this implementation with code to handle the error appropriately.abort() 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 persistent store is not accessible;* The schema for the persistent store is incompatible(冲突) with current managed object model.Check the error message to determine what the actual problem was.If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.If you encounter schema incompatibility errors during development, you can reduce their frequency by:* Simply deleting the existing store:[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]* Performing automatic lightweight migration by passing the following dictionary as the options parameter:@{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES}Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.*/NSLog(@"Unresolved error %@, %@", error, [error userInfo]);abort();}    return _persistentStoreCoordinator;
}

这里同样是 lazy initialization的初始化方式。

使用托管对象模型创建统筹者对象后,将持久化仓库添加到自己的管辖范围之下,添加的仓库必须指定数据的保存方式,如存储路径,存储的类型(SQLite数据库,XML文件,二进制文件,内存等)。另外下列方法给出Documents目录的路径:

- (NSURL *)applicationDocumentsDirectory {return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}


托管对象模型的getter方法:

- (NSManagedObjectModel *)managedObjectModel {// lazy initialization的getter方法if (_managedObjectModel != nil) {return _managedObjectModel;}// momd文件由xcdatamodeld生成,用来初始化对象模型对象NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreDataRef" withExtension:@"momd"];_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];return _managedObjectModel;
}

同样是 lazy initialization的初始化方式。

数据模型根据Bundle中的momd目录下的文件创建:


Bundle中的momd目录由项目中的xcdatamodeld文件加载后生成,xcdatamodeld文件的内容就是一个对象图,可以让我们更清晰直观地设计对象模型。


至此,所有由CoreData自动引入的代码分析完毕。

事实上,如果不想使用CoreData模板来导入该框架,那么肯定要理解上述代码的作用,才能灵活地在项目中加入CoreData框架。




这篇关于CoreData模板代码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1004249

相关文章

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

基于Java实现模板填充Word

《基于Java实现模板填充Word》这篇文章主要为大家详细介绍了如何用Java实现按产品经理提供的Word模板填充数据,并以word或pdf形式导出,有需要的小伙伴可以参考一下... Java实现按模板填充wor编程d本文讲解的需求是:我们需要把数据库中的某些数据按照 产品经理提供的 word模板,把数据

python多进程实现数据共享的示例代码

《python多进程实现数据共享的示例代码》本文介绍了Python中多进程实现数据共享的方法,包括使用multiprocessing模块和manager模块这两种方法,具有一定的参考价值,感兴趣的可以... 目录背景进程、进程创建进程间通信 进程间共享数据共享list实践背景 安卓ui自动化框架,使用的是

SpringBoot生成和操作PDF的代码详解

《SpringBoot生成和操作PDF的代码详解》本文主要介绍了在SpringBoot项目下,通过代码和操作步骤,详细的介绍了如何操作PDF,希望可以帮助到准备通过JAVA操作PDF的你,项目框架用的... 目录本文简介PDF文件简介代码实现PDF操作基于PDF模板生成,并下载完全基于代码生成,并保存合并P

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st