coredata 及 Magical Record

[http://blog.csdn.net/jiangshurunhe/article/details/10304309]


先简单的说一下coredata
coredata的五个模块:
1, Managed Object Model
Managed Object Model 是描述应用程序的数据模型,这个模型包含实体(Entity),特性(Property),读取请求(Fetch Request)等。(下文都使用英文术语。)
应用程序先创建或读取模型文件(后缀为xcdatamodeld)生成 NSManagedObjectModel 对象。Document应用程序是一般是通过 NSDocument 或其子类 NSPersistentDocument)从模型文件(后缀为 xcdatamodeld)读取。


2,Managed Object Context
Managed Object Context 参与对数据进行各种操作的整个过程,它持有 Managed Object。我们通过它来监测 Managed Object。监测数据对象有两个作用:支持 undo/redo 以及数据绑定。这个类是最常被用到的


3,Persistent Store Coordinator
Persistent Store Coordinator 负责从数据文件(xml, sqlite,二进制文件等)中读取数据生成 Managed Object,或保存 Managed Object 写入数据文件,处理底层的对数据文件的读取与写入。一般我们无需与它打交道。


4,Managed Object
Managed Object 数据对象,与 Managed Object Context 相关联。


5,Controller
一般都是通过 control+drag 将 Managed Object Context 绑定到它们,这样我们就可以在 nib 中可视化地操作数据。


模型有点像数据库的表结构,里面包含 Entry, 实体又包含三种 Property:Attribute(属性),RelationShip(关系), Fetched Property(读取属性)。Model class 的名字多以 "Description" 结尾。我们可以看出:模型就是描述数据类型以及其关系的。


主要的 Model class 有:


Model Classes
Managed Object Model NSManagedObjectModel数据模型
Entity NSEntityDescription抽象数据类型,相当于数据库中的表
Property NSPropertyDescriptionEntity 特性,相当于数据库表中的一列
  > Attribute NSAttributeDescription基本数值型属性(如Int16, BOOL, Date等类型的属性)
  > Relationship NSRelationshipDescription属性之间的关系
  > Fetched Property NSFetchedPropertyDescription查询属性(相当于数据库中的查询语句)


1)Entity - NSEntityDescription
Entity 相当于数据库中的一个表,它描述一种抽象数据类型,其对应的类为 NSManagedObject 或其子类。


NSEntityDescription 常用方法:
+insertNewObjectForEntityForName:inManagedObjectContext: 工厂方法,根据给定的 Entity 描述,生成相应的 NSManagedObject 对象,并插入 ManagedObjectContext 中。
-managedObjectClassName 返回映射到 Entity 的 NSManagedObject 类名
-attributesByName 以名字为 key, 返回 Entity 中对应的 Attributes
-relationshipsByName 以名字为 key, 返回 Entity 中对应的 Relationships


2)Property - NSPropertyDescription
Property 为 Entity 的特性,它相当于数据库表中的一列,或者 XML 文件中的 value-key 对中的 key。它可以描述实体数据(Attribute),Entity之间的关系(RelationShip),或查询属性(Fetched Property)。


 > Attribute - NSAttributeDescription
Attribute 存储基本数据,如 NSString, NSNumber or NSDate 等。它可以有默认值,也可以使用正则表达式或其他条件对其值进行限定。一个属性可以是 optional 的。
 
 > Relationship - NSRelationshipDescription 
Relationship 描述 Entity,Property 之间的关系,可以是一对一,也可以是一对多的关系。 


 > Fetched Property - NSFetchedPropertyDescription
Fetched Property 根据查询谓词返回指定 Entity 的符合条件的数据对象。


Managed Object - NSManagedObject
Managed Object 表示数据文件中的一条记录,每一个 Managed Object 在内存中对应 Entity 的一个数据表示。Managed Object 的成员为 Entity 的 Property 所描述。


每一个 Managed Object 都有一个全局 ID(类型为:NSManagedObjectID)。Managed Object 会附加到一个 Managed Object Context,我们可以通过这个全局 ID 在 Managed Object Context 查询对应的 Managed Object。
NSManagedObject 常用方法
-entity 获取其 Entity
-objectID 获取其 Managed Object ID
-valueForKey: 获取指定 Property 的值
-setValue: forKey: 设定指定 Property 的值
> Managed Object Context - NSManagedObjectContext


Managed Object Context 的作用相当重要,对数据对象进行的操作都与它有关。当创建一个数据对象并插入 Managed Object Context 中,Managed Object Context 就开始跟踪这个数据对象的一切变动,并在合适的时候提供对 undo/redo 的支持,或调用 Persistent Store Coordinato 将变化保存到数据文件中去。


通常我们将 controller 类与 Managed Object Context 绑定,这样就方便我们动态地生成,获取数据对象等。
NSManagedObjectContext 常用方法
-save: 将数据对象保存到数据文件
-objectWithID: 查询指定 Managed Object ID 的数据对象
-deleteObject: 将一个数据对象标记为删除,但是要等到 Context 提交更改时才真正删除数据对象
-undo 回滚最后一步操作,这是都 undo/redo 的支持
-lock 加锁,常用于多线程以及创建事务。同类接口还有:-unlock and -tryLock
-rollback 还原数据文件内容
-reset 清除缓存的 Managed Objects。只应当在添加或删除 Persistent Stores 时使用
-undoManager 返回当前 Context 所使用的 NSUndoManager
-assignObject: toPersistantStore: 由于 Context 可以管理从不同数据文件而来的数据对象,
这个接口的作用就是指定数据对象的存储数据文件(通过指定 PersistantStore 实现)
-executeFetchRequest: error: 执行 Fetch Request 并返回所有匹配的数据对象
> Persistent Store Coordinator - NSPersistentStoreCoordinator
使用 Core Data document 类型的应用程序,通常会从磁盘上的数据文中中读取或存储数据,这写底层的读写就由 Persistent Store Coordinator 来处理。一般我们无需与它直接打交道来读写文件,Managed Object Context 在背后已经为我们调用 Persistent Store Coordinator 做了这部分工作。
NSPersistentStoreCoordinator 常用方法
-addPersistentStoreForURL:configuration:URL:options:error:装载数据存储,对应的卸载数据存储的接口为 -removePersistentStore:error:
-migratePersistentStore:toURL:options:withType:error:迁移数据存储,效果与 "save as"相似,但是操作成功后,
迁移前的数据存储不可再使用
-managedObjectIDForURIRepresentation: 返回给定 URL所指示的数据存储的 object id,如果找不到匹配的数据存储则返回 nil
-persistentStoreForURL: 返回指定路径的 Persistent Store
-URLForPersistentStore: 返回指定 Persistent Store 的存储路径
> Persistent Document - NSPersistentDocument
NSPersistentDocument 是 NSDocument 的子类。 multi-document Core Data 应用程序使用它来简化对 Core Data 的操作。通常使用 NSPersistentDocument 的默认实现就足够了,它从 Info.plist 中读取 Document types 信息来决定数据的存储格式(xml,sqlite, binary)。
NSPersistentDocument 常用方法
-managedObjectContext 返回文档的 Managed Object Context,在多文档应用程序中,每个文档都有自己的 Context。
-managedObjectModel 返回文档的 Managed Object Model




用Magical Record,使Core Data获取数据更便捷!


   Magic Record是Saul Mora写的一个开源的类库,目的是使得Core Data使用起来更加的便捷容易。这个库的开发灵感来自于Ruby on Rails(Web 应用程序框架)中的active record模式。这个教程将给你讲述如何在你的app应用中使用 Magic Record!


   为什么选择Magical Record?如果你已经开发过iOS或者OS X 一段时间了的话,你就有机会尝试一下Core Data 堆栈(stack)了。这意味着你知道建立一个Core Data是相当麻烦的,主要是因为Core Data有冗长的语法所以在使用起来可能会有点复杂。例如,从一个 persistentstore(持久化存储)中获取数据是非常复杂的,尤其是再对比一下Ruby on Rails是如何获取数据的。


  向你展示Magic Record最好的方式就是使用这个强大的库来创建一个应用。你将发现使用Magic Record是多么的容易。我们将向你讲述用Core Data和Magic Record来创建项目需要涉及哪些内容。用户可以获取,创建,更新,删除数据的方法。
  既然你已经阅读到了这里,我假定你已经很熟悉iOS的开发,且对Core Data有了一些基本的认识。在这篇文章中,我将主要关注应用的Core Data方向,对代码的其余部分我将不详细赘述。


Step 1: 启动项目
  用Single View Application模板来创建一个项目,把它命名为Magic Notes(随便命名的),设置设备为iPhone, 勾选Use Automatic Reference Counting使ARC能够正常使用。在这篇教程中,我们不使用Storyboards 或者 Unit Tests.


Step 2: 添加Magic Record 
  既然我们在这个项目中使用了Core Data,就不要忘记把Core Data框架链接你的项目中。这是一篇高阶教程,所以在此不赘述如何去做这个工作。
   添加Magic Record库到你的项目中并不需要任何神奇的工作。从GitHub中下载最新的版本,打开 archive,拖拽名为MagicalRecord到你的Xcode项目中,勾选Copy times into destination gout's folder(if needed)选框,以保证复制文件夹的内容到你的项目中,不要忘记勾选Add to target ,也可以用git submodule add,用git工具比较好,更新代码比较简单,一行命令就可以了:git submodule update。你也可以使用CocoaPods(一个用来管理Xcode依赖库的项目)来管理MagicalRecord。
   
   确保可以在你的类中使用Magic Record, 我们需要导入一个头文件,CoreData+MagicRecord.h。在本节教程中使用Magic Record较为频繁,所以更加方便的办法是把这个import语句放到项目的Prefix.pch文件中。这会确保Magic Record可以在你的项目的任何类中访问。
   所有的Magic Record方法都默认带有前缀MR_,你可以在Prefix.pch文件中添加一行#define MR_SHORTHAND的代码来避免写MR_前缀。关键的一点是你必须把这行代码放到import "CoreData+MagicalRecord.h"这行代码的前面。


#ifdef __OBJC__ 
    #import <UIKit/UIKit.h> 
    #import <Foundation/Foundation.h> 
    #define MR_SHORTHAND 
    #import "CoreData+MagicalRecord.h" 
#endif
复制代码


Step 3: 创建一个Core Data Model(被管理对象模型)
  在建立Core Data stack前,我们需要创建一个Core Data Model(new filte --> core data --> data module --> add Entity)。这个项目的Core Data Model非常简单,就一个名为Note的实体。在attributes中加四个属性,date, title, body和keywords,Title, body, 和keywords 的类型为string,而date的类型为date。
  开始创建一个新的Core Data Model,把它命名为MagicalNotes。创建一个Note实体并且添加四个属性作为outlined)。


  在我们继续下一步之前,我们还需要为Note实体创建一个自定义的NSManagedObject子类。这是非常有用的,选中对应的Entity, 点击顶部菜单Editor-->Create NSManagedObject SubClass,会自动根据Entity 创建NSManagedObject class。


Step 4: 创建Core Data Stack
   如果你不用提供任何Xcode模板的话,建立Core Data stack将需要大量的工作。但是如果使用了Magic Record,就没那么麻烦了。在application:didFinishLaunchingWithOptions: 应用的一个方法中添加如下的代码片段:
[MagicalRecord setupCoreDataStack];([MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"note(项目名).db"];)
   
   这就是所有需要做的了。Magic Record创建store的名字默认跟你项目的名字是一样的。不过你可以调用setupCoreDataStackWithStoreNamed方法来自定义存储的名字:替换和传递store的名字。
   Magical Record 可以在主线程上实例化 managed object context(被管理对象上下文), a persistent store coordinator (持久化存储协调器)and a managed object model(被管理对象模型).
提示:Magical Record 还有一个内置的功能:纪录Core Data信息和错误到console中。第一次构建和运行完你的程序后看下console。console中的日志准确的纪录了Magic Record在后台做了哪些工作。


Step 5: 搭建基础框架
       在我们开始创建新的笔记的时候,我们首先要做一些繁重的工作。重新回顾一下你的应用委托的application:didFinishlaunchingWithOptions方法,用main view controller(主视图控制类)作为root view controller(根视图控制类)来初始化一个navigation controller(导航控制类)。来看一下这个方法的完整实现:
  在main view controller的implementation文件中,添加一个名为sourcesFrc的私有属性到类扩展的顶端。保证属性的类型是NSFetchedResultsController. sourcesFrc属性将会存储那些我们在数据库中获取到的笔记,并且我们将它作为table view的数据来源。
下面是常用的代码:


+ (NSFetchedResultsController *) fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath delegate:(id<NSFetchedResultsControllerDelegate>)delegate;


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return sourcesFrc.fetchedObjects.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell"; 
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; 
        // Configure Cell 
        [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; 
    } 
    // Fetch Note 
    Note *note =  sourcesFrc.fetchedObjects[indexPath.row]; 
    // Configure Cell 
    [cell.textLabel setText:[note title]]; 
    [cell.detailTextLabel setText:[note keywords]]; 
    return cell;
}


 #pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
if (type == NSFetchedResultsChangeInsert) {
[self.tableView insertRowsAtIndexPaths:@[newIndexPath]
 withRowAnimation:UITableViewRowAnimationFade];
} else if (type == NSFetchedResultsChangeDelete) {
[self.tableView deleteRowsAtIndexPaths:@[indexPath]
 withRowAnimation:UITableViewRowAnimationFade];
} else if (type == NSFetchedResultsChangeUpdate) {
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
 withRowAnimation:UITableViewRowAnimationNone];
} else if (type == NSFetchedResultsChangeMove) {
[self.tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
}
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
if (!sourcesFrc.fetchedObjects.count) {
  [_tableView reloadData];
}
}


- (void)remove:(JSONCompletionBlock)completion
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Source *_source = [self inContext:localContext];
[_source deleteEntity];
}];
}
删除方法: [_note deleteEntity];


- (void)addOneObject
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Note *_note = [Note createInContext:localContext];
_note.title = @"test";
}];
}
特别注意一点:module必须是在context中操作,所有的edit/add/都是在context中操作的,一般最好是用NSFetchedResultsController来处理数据,


   Magical Record有比我在这篇文章中显示给你的更多的内容。发布2.0版本以来。Magical Record可以处理nested contexts,它提供了对iCloud的支持,和线程处理方面的内容。这篇文章的主要目的是向你展示Core Data并不是很难处理的,Saul Mora 用Magical Record向你表明了这点。

你可能感兴趣的:(coredata 及 Magical Record)