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

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)

HTML5实现的移动端购物车自动结算功能示例代码

《HTML5实现的移动端购物车自动结算功能示例代码》本文介绍HTML5实现移动端购物车自动结算,通过WebStorage、事件监听、DOM操作等技术,确保实时更新与数据同步,优化性能及无障碍性,提升用... 目录1. 移动端购物车自动结算概述2. 数据存储与状态保存机制2.1 浏览器端的数据存储方式2.1.