CoreData模板代码分析

来源:互联网 发布:js window.open 编辑:程序博客网 时间:2024/06/02 11:26

之前写过一篇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.sqlite    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataRef.sqlite"];        NSError *error = nil;        // 通过托管对象模型来初始化持久化仓库统筹者    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];        // 根据配置选项将storeURL指定的存储介质(sqlite,XML,二进制文件,内存等)添加到统筹者的管辖范围内    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType                                                   configuration:nil                                                             URL:storeURL                                                         options:nil                                                           error:&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框架。




0 0
原创粉丝点击