Core Data & MagicalRecord


iOS 本地数据持久化存储:

一、plist

二、归档

三、NSUserDefaults 

四、NSFileManager

五、数据库

1、CoreData概述

CoreData是苹果自带的管理数据库的工具。使用Core Data有不少缘由,其中最简单的一条就是:它能让你为Model层写的代码的行数减小为原来的50%到70%。 这归功于以前提到的Core Data的特性。更妙的是,对于上述特性你也既不用去测试,也不用花功夫去优化。

Core Data拥有成熟的代码,这些代码经过单元测试来保证品质。经过了几个版本的发布,已经被高度优化。 它能利用Model层的信息和运行时的特性,而不经过程序层的代码实现。 除了提供强大的安全支持和错误处理外,它还提供了最优的内存扩展性,可实现有竞争力的解决方案。不使用Core Data的话,你须要花很长时间来起草本身的方案,解决各类问题,这样作效率不高。

另外:CoreData主要是iOS对SQLite数据库的封装。CoreData有 对象-关系 的映射的功能,能把OC的对象存储成数据库或 xml 等。若是数据存储使用的是coreData,那么读取时能够不使用 SQLite 语句。

2、关于Core Data常见的误解

一、 Core Data不是一个关系型数据库,也不是关系型数据库管理系统(RDBMS)。
Core Data 为数据变动管理、对象存储、对象读取恢复的功能提供了支持。 它可使用SQLite做为持久化存储的类型。 它自己并非一个数据库(这点很重要,好比,你可使用Core Data来记录数据变动,管理数据,但并不能用它向文件内存储数据)。

2 、它并不能取代你写代码的工做。虽然能够纯粹使用XCode的数据建模工具和Interface Builder来编写复杂程序,但在更多的程序中,你都本身动手写代码。

3、CoreData 的核心概念

关键的概念图









(1)NSManagedObjectModel 托管对象模型(MOM)

(用来加载Core Data数据模型文件,全部的数据模型能够所有加载到这个对象中。)





这个MOM由实体描述对象,即NSEntityDescription实例的集合组成,实体描述对象介绍见下面第7条。

做用:添加实体的属性,创建属性之间的关系

(2)NSManagedObjectContext 托管对象上下文(MOC)

(用于操做数据模型(对象),并检测数据模型(对象)的变化。)



在概念图2中,托管对象上下文(MOC)经过持久化存储协调器(PSC)从持久化存储(NSPersistentStore)中获取对象时,这些对象会造成一个临时副本在MOC中造成一个对象集合,该对象集合包含了对象以及对象彼此之间的一些关系。咱们能够对这些副本进行修改,而后进行保存,而后MOC会经过 PSC 对 NSPersistentStore 进行操做,持久化存储就会发生变化。

CoreData中的NSManagedObjectModel 托管对象的数据模型(MOM),经过 MOC 进行注册。MOC有插入、删除以及更新数据模型等操做,并提供了撤销和重作的支持。

做用:插入数据,更新数据,删除数据

(3)NSPersistentStoreCoordinator 持久化存储协调器(PSC)

(数据持久化存储协调器,负责调度上层与底层对数据的操做。)





在应用程序和外部数据存储的对象之间提供访问通道的框架对象集合统称为持久化堆栈(persistence stack)。在堆栈顶部的是托管对象上下文(MOC),在堆栈底部的是持久化对象存储(persistent object stores)。在托管对象上下文和持久化对象存储之间即是持久化存储协调器(PSC)。应用程序经过类NSPersistentStoreCoordinator的实例访问持久化对象存储。

持久化存储协调器为一或多个托管对象上下文提供一个访问接口,使其下层的多个持久化存储能够表现为单一一个聚合存储。一个托管对象上下文能够基于持久化存储协调器下的全部数据存储来建立一个对象图。持久化存储协调器只能与一个托管对象模型(MOM)相关联。

(4)NSManagedObject 托管对象(MO)

(具体的数据模型对象。)



托管对象必须继承自NSManagedObject或者NSManagedObject的子类。NSManagedObject可以表述任何实体。它使用一个私有的内部存储,以维护其属性,并实现托管对象所需的全部基本行为。托管对象有一个指向实体描述的引用。NSEntityDescription 实体描述表述了实体的元数据,包括实体的名称,实体的属性和实体之间的关系。

(5)Controller 控制器

概念图1中绿色的 Array Controller,Object Controller,Tree Controller 这些控制器,通常都是经过 control+drag 将 Managed Object Context 绑定到它们,这样咱们就能够在 nib 中可视化地操做数据

(6)NSFetchRequest 获取数据请求

使用托管对象上下文来检索数据时,会建立一个获取请求(fetch request)。相似Sql查询语句的功能。

(7)NSEntityDescription 实体描述

(模型描述类,可以实例化获得具体的数据模型对象。)



实体描述对象提供了一个实体的元数据,包括实体名(Name),类名(ClassName),属性(Properties)以及实体属性与其余实体的一些关系(Relationships)等。

(8).xcdatamodeld





里面是.xcdatamodeld文件,用数据模型编辑器编辑,编译后为.momd或.mom文件。

咱们能够选中咱们的应用程序(路径相似为/Users/Childhood/Library/Application Support/iPhone Simulator/7.1/Applications/005D926F-5763-4305-97FE-AE55FE7281A4),右键显示包内容,咱们看到是这样的。



4、Core Data运做机制



1,应用程序先建立或读取模型文件(后缀为xcdatamodeld)生成 NSManagedObjectModel 对象。从模型文件(后缀为 xcdatamodeld)读取。


2,而后生成 NSManagedObjectContext 和 NSPersistentStoreCoordinator 对象,前者对用户透明地调用后者对数据文件进行读写。


3,NSPersistentStoreCoordinator 负责从数据文件(xml, sqlite,二进制文件等)中读取数据生成 Managed Object,或保存 Managed Object 写入数据文件。
4,NSManagedObjectContext 参与对数据进行各类操做的整个过程,它持有 Managed Object。咱们经过它来监测 Managed Object。监测数据对象有两个做用:支持 undo/redo 以及数据绑定。这个类是最常被用到的。


5,Array Controller, Object Controller, Tree Controller 这些控制器通常与 NSManagedObjectContext 关联,所以咱们能够经过它们在 nib 中可视化地操做数据对象。

5、代码步骤

一、加载Core Data文件



二、将数据的存储方式设定为数据库



三、操做数据:增长



四、操做数据:查询



五、操做数据:修改



六、操做数据:删除

css

 

 

 

 


MagicalRecord  https://github.com/magicalpanda/MagicalRecordhtml

注意:  MagicalRecord 在 ARC 下运做,Core Data 是 ORM 方案,听说带来的麻烦比好处多,且 Core Data 创建的表没有主键,但对于对数据库没有性能要求,进行简单的数据操做彻底够用,能简化无数的代码量.ios

MagicalRecord

In software engineering, the active record pattern is a design pattern found in software that stores its data in relational databases. It was named by Martin Fowler in his book Patterns of Enterprise Application Architecture. The interface to such an object would include functions such as Insert, Update, and Delete, plus properties that correspond more-or-less directly to the columns in the underlying database table.git

在软件工程中,对象与数据库中的记录实时映射是一种设计模式,在处理关系型数据库的的软件中多有出现.这种设计模式被记录在 Martin Fowler 的<Patterns of Enterprise Application Architecture> 中.被操做对象的接口应该包含增删改查的方法,基本属性很少很多的恰好与数据库中的一条记录相对应.github

Active record is an approach to accessing data in a database. A database table or view is wrapped into a class; thus an object instance is tied to a single row in the table. After creation of an object, a new row is added to the table upon save. Any object loaded gets its information from the database; when an object is updated, the corresponding row in the table is also updated. The wrapper class implements accessor methods or properties for each column in the table or view.sql

实时映射记录是一种操做数据库的方式,一个数据库的表被封装成一个对象;这个对象中的一个实例会对应着该表中的一条记录.当建立一个对象时,一条记录也被插入到表中并保存起来.任何被加载的对象中的属性信息都从数据库中读取;当一个对象更新时,这个数据库表中对应的记录也会更新.这个被封装的类实现了实时操做的方法,且其属性一一对应于数据库中表的属性.数据库

Wikipedia设计模式

MagicalRecord was inspired by the ease of Ruby on Rails' Active Record fetching. The goals of this code are:数组

  • Clean up my Core Data related code
  • Allow for clear, simple, one-line fetches
  • Still allow the modification of the NSFetchRequest when request optimizations are needed

MagicalRecord 灵感来自于简洁的Ruby语言中 Rails' Active Record 查询方式. MagicalRecord 这个开源库的核心思想是:安全

  • 清除 Core Data 相关的代码
  • 简洁的清除,简单的一行搜索记录的功能
  • 固然容许使用NSFetchRequest,当存在着复杂的搜索条件时

 

拙劣的翻译请勿见怪,如下是我在最新的 Xcode 5.1 开 ARC 的环境下的使用教程.

 

1. 将 MagicalRecord 文件夹拖入到工程文件中,引入 CoreData.frame 框架

2. 在 .pch 文件中引入头文件 CoreData+MagicalRecord.h

注:只能在.pch文件中引头文件,不然没法经过编译

3. 建立 Model.xcdatamodeld 文件,并建立一个 Student 的 ENTITIES,最后建立出 Student 类

4. 在 Appdelete.m 文件中写如下代码

 

如下是增删改查的基本操做,但注意一点,在作任何的数据库操做以前,请先初始化如下,在Appdelete载入时初始化一次便可,不然找不到数据库而崩溃,你懂的.

    //设置数据库名字
    [MagicalRecord setupCoreDataStackWithStoreNamed:@"Model.sqlite"];

 

增长

增长一条记录
    Student *person = [Student MR_createEntity];
    person.name = @"Y.X.";
    [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];

注意:建立了对象后是须要执行存储操做的

查询

查询全部的记录

    NSArray *students = [Student MR_findAll];


根据某个属性某个条件查询

    NSArray *students = [Student MR_findByAttribute:@"name" withValue:@"Y.X."];

 

根据排序取得搜索结果

    NSArray *students = [Student MR_findAllSortedBy:@"name" ascending:YES];

 

我不一一列举了,查看一下头文件就知道了.

查询全部记录

+ (NSArray *) MR_findAll;

根据上下文句柄查询全部记录
+ (NSArray *) MR_findAllInContext:(NSManagedObjectContext *)context;

根据某个属性排序查询全部记录
+ (NSArray *) MR_findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending;

根据某个属性排序以及上下文操做句柄查询全部记录
+ (NSArray *) MR_findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context;

根据某个属性排序用谓词来查询记录
+ (NSArray *) MR_findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm;

根据某个属性排序以及上下文操做句柄用谓词来查询记录
+ (NSArray *) MR_findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;

根据谓词查询
+ (NSArray *) MR_findAllWithPredicate:(NSPredicate *)searchTerm;

根据谓词以及上下文操做句柄来查询
+ (NSArray *) MR_findAllWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;

如下都是查询一个对象时的操做,与上面重复,不一一赘述
+ (instancetype) MR_findFirst;
+ (instancetype) MR_findFirstInContext:(NSManagedObjectContext *)context;
+ (instancetype) MR_findFirstWithPredicate:(NSPredicate *)searchTerm;
+ (instancetype) MR_findFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context;
+ (instancetype) MR_findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending;
+ (instancetype) MR_findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context;
+ (instancetype) MR_findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes;
+ (instancetype) MR_findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes inContext:(NSManagedObjectContext *)context;
+ (instancetype) MR_findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending andRetrieveAttributes:(id)attributes, ...;
+ (instancetype) MR_findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context andRetrieveAttributes:(id)attributes, ...;
+ (instancetype) MR_findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue;
+ (instancetype) MR_findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context;
+ (instancetype) MR_findFirstOrderedByAttribute:(NSString *)attribute ascending:(BOOL)ascending;
+ (instancetype) MR_findFirstOrderedByAttribute:(NSString *)attribute ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context;

修改

    NSArray *students = [Student MR_findByAttribute:@"name" withValue:@"Y.X."];
    for (Student *tmp in students) {
        tmp.name = @"Jane";
    }
    [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait];

注意:既然要修改首先得须要找到记录,根据条件匹配找到记录,而后修改,而后保存

 

删除

    NSArray *students = [Student MR_findByAttribute:@"name" withValue:@"Frank"];
    for (Student *tmp in students) {
        [tmp MR_deleteEntity];
    }
    [[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; 

注意:既然要删除首先得须要找到记录,根据条件匹配找到记录,而后删除,而后保存

 

心得体会

若是项目中对于操做数据库没有性能要求请使用 CoreData 相关的开源库吧.

CoreData 操做较为复杂, MagicalRecord 有着不少的特性,好比能够根据设置在主线程或者子线程中进行操做,方便快捷,能入榜最佳10大开源库自有其独到的地方,会使用 MagicalRecord 须要具有必定的 CoreData 相关知识,本人也只是现学现用,但深知其能够为开发带来的好处,使用数据库的朋友有着以下的一些选择.

1. SQLite3                 C函数形式(本人以前作过干嵌入式开发,即便是这样也不推荐使用面向过程毫无对象概念的SQLite3,有更好的方式为何不用呢?)

2. FMDB                    对SQLite3的封装,有着对象的概念,熟悉SQ语句的朋友可使用,但尚未作到对象与记录实时对应

3. CoreData              他作到了对象与记录实时对应关系,使用其自身的搜索体系(不用SQ语言),但其基本的操做以及配置让人望而却步

4. MagicalRecord      对 CoreData 官方的用法进行了人性化的封装,用过 CoreData 基本操做再使用 MagicalRecord 会深有体会

5. ObjectiveRecord   也是对 CoreData 的人性化封装,使用更加傻瓜,但傻瓜的代价就是牺牲了一些更强大的功能,在Github上搜索关键字便可

 

 

附录:

1.默认的就是在后台存储的,不会阻塞主线程

我在 CoreData+MagicalRecord.h 文件中定义了宏 MR_SHORTHAND ,因此在方法中不须要 MR_ 前缀了

如下为代码(提供block来通知存储成功,异步操做)

如下为打印信息

----------------------------------------------------------------------------------------------------------------------------------------------------

2014-03-13 11:17:43.616 StudyMagicalRecord[26416:60b] +[NSManagedObjectContext(MagicalRecord) MR_contextWithStoreCoordinator:](0x2f4498) -> Created Context UNNAMED
2014-03-13 11:17:43.616 StudyMagicalRecord[26416:60b] +[NSManagedObjectContext(MagicalRecord) MR_setRootSavingContext:](0x2f4498) Set Root Saving Context: <NSManagedObjectContext: 0xe74d910>
2014-03-13 11:17:43.617 StudyMagicalRecord[26416:60b] +[NSManagedObjectContext(MagicalRecord) MR_newMainQueueContext](0x2f4498) Created Main Queue Context: <NSManagedObjectContext: 0xe74e040>
2014-03-13 11:17:43.617 StudyMagicalRecord[26416:60b] +[NSManagedObjectContext(MagicalRecord) MR_setDefaultContext:](0x2f4498) Set Default Context: <NSManagedObjectContext: 0xe74e040>
2014-03-13 11:17:43.618 StudyMagicalRecord[26416:60b] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xe74e040) → Saving <NSManagedObjectContext (0xe74e040): *** DEFAULT ***> on *** MAIN THREAD ***
2014-03-13 11:17:43.618 StudyMagicalRecord[26416:60b] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xe74e040) → Save Parents? 1
2014-03-13 11:17:43.619 StudyMagicalRecord[26416:60b] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xe74e040) → Save Synchronously? 0
2014-03-13 11:17:43.619 StudyMagicalRecord[26416:60b] time
2014-03-13 11:17:43.622 StudyMagicalRecord[26416:60b] -[NSManagedObjectContext(MagicalRecord) MR_contextWillSave:](0xe74e040) Context DEFAULT is about to save. Obtaining permanent IDs for new 1 inserted objects
2014-03-13 11:17:43.623 StudyMagicalRecord[26416:60b] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xe74d910) → Saving <NSManagedObjectContext (0xe74d910): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
2014-03-13 11:17:43.624 StudyMagicalRecord[26416:60b] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xe74d910) → Save Parents? 1
2014-03-13 11:17:43.624 StudyMagicalRecord[26416:60b] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0xe74d910) → Save Synchronously? 0
2014-03-13 11:17:43.625 StudyMagicalRecord[26416:1303] -[NSManagedObjectContext(MagicalRecord) MR_contextWillSave:](0xe74d910) Context BACKGROUND SAVING (ROOT) is about to save. Obtaining permanent IDs for new 1 inserted objects
2014-03-13 11:17:43.626 StudyMagicalRecord[26416:1303] __70-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:]_block_invoke25(0xe74d910) → Finished saving: <NSManagedObjectContext (0xe74d910): *** BACKGROUND SAVING (ROOT) ***> on *** BACKGROUND THREAD ***
2014-03-13 11:17:43.627 StudyMagicalRecord[26416:60b] YES

----------------------------------------------------------------------------------------------------------------------------------------------------

2.如何关闭 MagicalRecord 提供的打印信息?

修改 MagicalRecord.h 23 行处的值,把 0 改成 1 便可.

 

 
 
 
 
 
 

Magical Record是用来操做Core Data的一个第三方工具,在介绍Magical Record 以前必需要先了解一下Core Data的基本概念

Core Data基本介绍

Core Data Stack

核心数据堆栈是由一个或多个与单个persistent store coordinator关联的managed object contexts 组成,而persistent store coordinator是和一个或多个persistent stores关联在一块儿。堆栈包含了CoreData的全部组件查询,建立,操做managed objects.
简单来讲包含了:

  • 一个包含了记录的persistent store.相似于数据库
  • 一个在本地数据和对象之间的persistent object store
  • 一个聚合了全部存储的persistent store coordinator
  • 一个描述实体的managed object model
  • 一个容器包含managed objectsmanaged object context容器

可能有点绕,不过一看图世界就清晰了

以下图:


 

Managed Object

Managed Object是一个模型对象(模型-视图-控制器的意义上),它表明了一个持久存储的记录。管理对象是实例NSManagedObject或子类NSManagedObject。

管理对象有一个实体的描述对象,告诉它表明着什么实体的引用。以这种方式,NSManagedObject能够表示任何实体不须要每一个实体的惟一的子类。若是要实现自定义行为,例如计算派生属性值,或者为了实现验证逻辑可使用一个子类。

仍是来看图:


 

Managed Object Model


 

Manage Context Object

Manage Context Object表明单个对象的空间,,在核心数据的应用程序。管理对象上下文的一个实例的NSManagedObjectContext。它的主要职责是管理管理对象的集合。这些管理对象表明一个或多个持久存储的一个内部一致的见解。上下文是在管理对象的生命周期核心做用。

上下文是在核心数据堆栈中的中心对象。这是你用它来建立和获取管理对象和管理撤消和恢复操做的对象。内的给定范围内,有至多一个被管理目标表明在永久存储器的任何给定的记录。


 

上下文被链接到一个父对象存储这一般是一个持久存储协调,但多是另外一个管理对象上下文。当你获取对象,上下文要求其父对象存储返回那些符合提取请求的对象。您对管理对象的修改,直到您保存的背景下不被提交到父store。

在某些应用中,你可能想保持独立组来管理对象和编辑这些对象的; 或者你可能须要执行使用一个上下文,同时容许用户与另外一个对象交互的后台操做

Persistent Store Coordinator

哎!翻译太累了。直接上图吧


 

这张图把这个的架构解释得很是清楚

Fetch Request


 

官方文档

开始使用Magical Record

导入MagicalRecord.h在项目的预编译文件*.pch中。这保证了能够全局访问所须要的头文件。

使用了CocoaPods或者MagicalRecord.framework,用以下方式导入:

// Objective-C #import <MagicalRecord/MagicalRecord.h> // Swift import MagicalRecord

若是是把源文件直接放到项目中,则直接#import "MagicalRecord.h"

接下里,在app delegate的某些地方,好比- applicationDidFinishLaunching: withOptions:或者-awakeFromNib,使用下面的某一个方法来配置MagicalRecord.

+ (void)setupCoreDataStack; + (void)setupAutoMigratingCoreDataStack; + (void)setupCoreDataStackWithInMemoryStore; + (void)setupCoreDataStackWithStoreNamed:(NSString *)storeName; + (void)setupCoreDataStackWithAutoMigratingSqliteStoreNamed:(NSString *)storeName; + (void)setupCoreDataStackWithStoreAtURL:(NSURL *)storeURL; + (void)setupCoreDataStackWithAutoMigratingSqliteStoreAtURL:(NSURL *)storeURL;

每次调用Core Data的堆栈的实例,提供给了这些实例的getter,setter方法。这些实例被MagicalRecord很好的管理,被识别为默认方式。

当经过DEBUG模式标识使用SQLite数据库,不建立新的model版原本改变model将会引发MagicalRecord自动的删除老的数据库而且自动的建立一个新的。这样能够节约不少时间--不须要每次都卸载重装app来让data model改变,确保你的app不是用的DEBUG模式:当删除app数据的时候不告诉用户真的是一种很糟糕的方式

在你的app退出以前,你应该调用类方法+cleanUp

[MagicalRecord cleanUp];

这将会清理MagicalRecord,好比自定义的错误处理,让经过MagicalRecord建立的Core Data堆栈为nil.

使用Managed Object Contexts

建立新的上下文

一些简单的类方法用来帮助快速的你建立新的上下文

    • [NSManagedObjectContext MR_newMainQueueContext]:
    • [NSManagedObjectContext MR_newPrivateQueueContext]:
    • [NSManagedObjectContext MR_newContextWithStoreCoordinator:…]: 容许你具体化persistent store coordinator为新的上下文,有一个NSPrivateQueueConcurrencyType

默认上下文

当使用CoreData,你将不断的和两个主要的对象打交道,NSManagedObjectNSManagedObjectContext.

MagicalRecord提升了一个简单的类方法来获取默认的NSManagedObjectContext,这个上下文贯穿了你的app始终,这个上下文的操做会在在主线程中进行,而且对于单线程的app比较适合

经过以下方式访问到默认的上下文:

NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext];

这个上下文将在MagicalRecord任何使用了上下文的方法中使用,可是没有提供一个具体的NSManagedObjectContext参数。

若是你须要建立一个再也不主线程中使员工的上下文,使用:

NSManagedObjectContext *myNewContext = [NSManagedObjectContext MR_newContext];

这种方式将会建立一个和default context有相同的对象和persistent store.安全在其余线程使用。它将会默认的将default context做为它的父上下文

若是你想默认让myNewContext实例化全部的fetch request.使用类方法的方式

[NSManagedObjectContext MR_setDefaultContext:myNewContext];

注意:高度建议default context使用类型为NSMainQueueConcurrencyType的上下文来建立并设置在主线程。

在后台线程中执行

MagicalRecord提供了方法来设置,协调上下文在后台线程中使用。后台保存操做受到了UIView动画使用Block的方式,但也存在了一些不一样

  • 在你对实体进行改变了的block,绝对不在主线程中执行

  • 单个的NSManagedObjectContext提供了block使用。

举个例子,你有一个Person实体,而且须要设置firstName和lastName,下面的代码展现了你怎样经过MagicalRecord来设置后台上下文进行使用。

Person *person = ...;

[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){ Person *localPerson = [person MR_inContext:localContext]; localPerson.firstName = @"John"; localPerson.lastName = @"Appleseed"; }];

在这个方法,具体的block提供了一个合适的上下文让你进行操做,不须要担忧去设置上下文,以便它告诉default context已经作了。而且应该更新,由于是在其余线程里面改变进行的。

当执行完了saveBlock,你能够在completion block作些操做

Person *person = ...;

[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext){ Person *localPerson = [person MR_inContext:localContext]; localPerson.firstName = @"John"; localPerson.lastName = @"Appleseed"; } completion:^(BOOL success, NSError *error) { self.everyoneInTheDepartment = [Person findAll]; }];

completion block在主线程中被调用,为了UI更新更安全。

建立实体

在默认的上下文中插入一个实体,以下:

Person *myPerson = [Person MR_createEntity];

在具体的上下文中插入一个实体
Person *myPerson = [Person MR_createEntityInContext:otherContext];

删除一个实体

在默认上下文中删除:
[myPerson MR_deleteEntity];

在具体上下文中删除:
[myPerson MR_deleteEntityInContext:otherContext];

截断全部实体在默认上下文
[Person MR_truncateAll];

截断全部实体在具体上下文
[Person MR_truncateAllInContext:otherContext];

查询实体

基本查找

MagicalRecord大多数方法是返回一个NSArray数组。

举例,若是你有一个person实体和department实体关联,你能够查询全部的person实体从persistent store经过以下方式实现:

NSArray *people = [Person MR_findAll];

传入一个具体的参数返回一个排序后的数组:

NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName" ascending:YES];

传入多个具体的参数返回一个排序后的数组

NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName,FirstName" ascending:YES];

传入多个不一样参数值获得排序结果,若是你不提供任何一个参数的默认值,就会默认使用你在model中的设置。

NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName:NO,FirstName" ascending:YES]; // OR NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName,FirstName:YES" ascending:NO];

若是你有一种惟一从数据库中查询单个对象的方法(好比做为惟一属性),你能够经过下面的方法:

Person *person = [Person MR_findFirstByAttribute:@"FirstName" withValue:@"Forrest"];

高级查找

若是想去具体化你的搜索,你可使用谓词

NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", @[dept1, dept2]]; NSArray *people = [Person MR_findAllWithPredicate:peopleFilter];

返回NSFetchRequest

NSPredicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments]; NSFetchRequest *people = [Person MR_requestAllWithPredicate:peopleFilter];

关于每一行的调用, NSFetchRequestNSSortDescriptor做为排序的标配。

自定有Requset

Predicate *peopleFilter = [NSPredicate predicateWithFormat:@"Department IN %@", departments]; NSFetchRequest *peopleRequest = [Person MR_requestAllWithPredicate:peopleFilter]; [peopleRequest setReturnsDistinctResults:NO]; [peopleRequest setReturnPropertiesNamed:@[@"FirstName", @"LastName"]]; NSArray *people = [Person MR_executeFetchRequest:peopleRequest];

查询实体数量

能够执行全部实体类型输血量在persistent store

NSNumber *count = [Person MR_numberOfEntities];

或者基于查询的数量
NSNumber *count = [Person MR_numberOfEntitiesWithPredicate:...];

这里有一组方法来返回NSUInteger而不是NSNumber

+ (NSUInteger) MR_countOfEntities; + (NSUInteger) MR_countOfEntitiesWithContext:(NSManagedObjectContext *)context; + (NSUInteger) MR_countOfEntitiesWithPredicate:(NSPredicate *)searchFilter; + (NSUInteger) MR_countOfEntitiesWithPredicate:(NSPredicate *)searchFilter inContext:(NSManagedObjectContext *)context;

聚合操做

NSNumber *totalCalories = [CTFoodDiaryEntry MR_aggregateOperation:@"sum:" onAttribute:@"calories" withPredicate:predicate]; NSNumber *mostCalories = [CTFoodDiaryEntry MR_aggregateOperation:@"max:" onAttribute:@"calories" withPredicate:predicate]; NSArray *caloriesByMonth = [CTFoodDiaryEntry MR_aggregateOperation:@"sum:" onAttribute:@"calories" withPredicate:predicate groupBy:@"month"];

在具体的上下文中查找实体

全部的 find, fetch, request方法都有一个inContext:,方法参数容许具体使用哪个上下文查询:

NSArray *peopleFromAnotherContext = [Person MR_findAllInContext:someOtherContext]; Person *personFromContext = [Person MR_findFirstByAttribute:@"lastName" withValue:@"Gump" inContext:someOtherContext]; NSUInteger count = [Person MR_numberOfEntitiesWithContext:someOtherContext];

存储实体

何时应该保存

总的来讲,当数据发生改变的时候应该保存到persistent store(s).一些应用选择在应用终止的时候才保存。然而,在大多数场景下是不须要的。事实上,只在应用终止的时候保存,会有数据丢失的风险。万一你的应用崩溃了怎么办?用户将丢失他对数据所作的改变。那样的话是一种至关糟糕的体验,能够简单的避免。

若是你以为执行保存话费了大量的时间,有几件事情须要考虑:

  • 1.在后台线程保存:MagicalRecord提升了很是简单的API来让改变的实例按顺序的在后台保存,举个例子:
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {

    // Do your work to be saved here, against the `localContext` instance // Everything you do in this block will occur on a background thread } completion:^(BOOL success, NSError *error) { [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }];
  • 2.将任务分解为多个小任务保存:好比大量数据的导入应被分解为小块,没有确切的原则来决定一次导入多少数据,你须要测量你应用的性能好比经过苹果的Instruments和tune.

解决长期运行中的保存

在iOS上

当应用在iOS上终止运行,有一个很小的机会去清理,保存数据到磁盘。若是你知道保存操做可能会花一段时间,最好的方式就是去申请一个额外的截止时间。好比:

UIApplication *application = [UIApplication sharedApplication]; __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) { // Do your work to be saved here } completion:^(BOOL success, NSError *error) { [application endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }];

确保仔细的读过[

使用模式

  • 保存数据
  • 查找实体
  • 导入数据
  • 线程安全

日志

MagicalRecord创建了在和Core Data交互的时候的日志。当错误发生的时候,这些错误将会被捕获。而且将打印到控制台。

日志被配置为输出调试信息(MagicalRecordLoggingLevelDebug)在deug编译的时候默认的,将会输出日志信息MagicalRecordLoggingLevelError在realease下。

日志经过[MagicalRecord setLoggingLevel:]配置,使用下面的几种预约义的日主等级。

  • MagicalRecordLogLevelOff:不开始日志
  • MagicalRecordLoggingLevelError:记录全部错误
  • MagicalRecordLoggingLevelWarn:记录警告和错误
  • MagicalRecordLoggingLevelInfo:记录日志有用的信息,错误,警告
  • MagicalRecordLoggingLevelDebug:全部调试信息
  • MagicalRecordLoggingLevelVerbose:日志冗长的诊断信息,有用的信息,错误,警告

日志默认等级是 MagicalRecordLoggingLevelWarn

关闭日志

大多数人而言,这个不须要,设置日志等级为MagicalRecordLogLevelOff将会保证再也不打印日志信息

甚至当使用了MagicalRecordLogLevelOff,快速检测检查可能被调用不管什么时候日志被调用。若是想绝对的关闭日志。你能够定义以下,当编译MagicalRecord的时候
#define MR_LOGGING_DISABLED 1

请注意:这个以后再增长源码到项目中才会起做用。你也能够增长MagicalRecord项目的OTHER_CFLAGS为-DMR_LOGGING_DISABLED=1

日志在2.3.0版本有问题,不能正常的显示到控制器

google到了解决的方法副在下面

For the development branch (version 2.3.0 and higher) of Magical Record logging seems to still not work correctly. When imported like this: pod 'MagicalRecord', :git => 'https://github.com/magicalpanda/MagicalRecord', :branch => 'develop' I have no logging output on my Xcode console. But I altered the post_install script of the Cocoapod. The following should enable logging: https://gist.github.com/Blackjacx/e5f3d62d611ce435775e With that buildsetting included in GCC_PREPROCESSOR_DEFINITIONS logging of Magical Record can be controlled in 2.3.0++ by using [MagicalRecord setLoggingLevel:]
  • 脚本:
post_install do |installer| installer.project.targets.each do |target| target.build_configurations.each do |config| # Enable the loggin for MagicalRecord # https://github.com/magicalpanda/MagicalRecord/wiki/Logging if target.name.include? "MagicalRecord" preprocessorMacros = config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] if preprocessorMacros.nil? preprocessorMacros = ["COCOAPODS=1"]; end preprocessorMacros << "MR_LOGGING_ENABLED=1" config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = preprocessorMacros end end end end

本身尝试遇到的坑

日记记录

2.3.0版本一样遇到了日志不能正常输出到控制台的问题,虽然可以拿到解决问题的脚步,可是本身在taget,buildsetting里面都设置了仍是没有用。本身对cocopods管理的原理还不是很明白。

上下文的坑

NSManagedObjectContext这个类是CoreData里面很是重要的类。它有父上下文和子上下文的概念。通过了漫长的爬坑,终于在苹果官方文档中找到了关于它详细的介绍。

这里只截取parent store这节来说

Managed object contexts有一个父存储,经过它来检索数据,提交改变

最开始在iOS5的以前,父存储一直是persistent store coordinator。在iOS5以后。父存储的类型能够是其余的Managed object contexts。可是最终的根context必须是`persistent store coordinator``。协调者提升被管理的对象模型,调用各类对数据库的请求。

若是父存储是一个Managed object contexts。查询,保存的操做是被父存储来协调的而不是persistent store coordinator。这种方式有两个好处,

  • 1.在其余线程中执行操做
  • 2.管理废弃的编辑,好比监视窗口、view
    第一种场景,父上下文可以经过不一样的线程从子中得到请求,

  • 重点部分:当在上下文中保存所作的改变的时候,改变只会被提交一次存储,若是有子的上下文,改变将会推到他的父上下文,改变不会直接保存到数据库,直到根上下文被保存才会保存到数据库(根管理对象的上下文的父上下文为空)。除此以外,父上下文在保存以前不会从子中拉取数据的改变。若是你想最后提交数据的改变,必须保存子上下文,这样就能够推到父上下文中。

测试代码

上下文的建立时经过线程来控制,也就是上下文和线程相关。[[NSThread currentThread] threadDictionary];返回的字典就是处理数据方面的。

if ([NSThread isMainThread]) { return [self MR_defaultContext]; } else { int32_t targetCacheVersionForContext = contextsCacheVersion; NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; NSManagedObjectContext *threadContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextKey]; NSNumber *currentCacheVersionForContext = [threadDict objectForKey:kMagicalRecordManagedObjectContextCacheVersionKey]; // 保证二者同时存在,或者同时不存在 NSAssert((threadContext && currentCacheVersionForContext) || (!threadContext && !currentCacheVersionForContext), @"The Magical Record keys should either both be present or neither be present, otherwise we're in an inconsistent state!"); // 不存在上下文 if ((threadContext == nil) || (currentCacheVersionForContext == nil) || ((int32_t)[currentCacheVersionForContext integerValue] != targetCacheVersionForContext)) { // 建立新的上下文 threadContext = [self MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]]; [threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey]; [threadDict setObject:[NSNumber numberWithInteger:targetCacheVersionForContext] forKey:kMagicalRecordManagedObjectContextCacheVersionKey]; } return threadContext; }

在配置的时候就会默认建立两种上下文,一个根上下文,和协调者直接通讯的,一个是主线程相关的默认上下文。默认上下文是根上下文的子。

  • 有必要说一说MR_saveWithBlock这个方法,本身在写的时候就犯错了。

开看看实现

- (void)MR_saveWithBlock:(void (^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion; { NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextWithParent:self]; [localContext performBlock:^{ [localContext MR_setWorkingName:NSStringFromSelector(_cmd)]; if (block) { block(localContext); } [localContext MR_saveWithOptions:MRSaveParentContexts completion:completion]; }]; }

是在当前的上下文中新建子而后经过子去保存,注意这里的保存方法有个参数MRSaveParentContexts,会连同父上下文一块儿一般,

在保存的方法中有一段:

// Add/remove the synchronous save option from the mask if necessary MRSaveOptions modifiedOptions = saveOptions; if (saveSynchronously) { modifiedOptions |= MRSaveSynchronously; } else { modifiedOptions &= ~MRSaveSynchronously; } // If we're saving parent contexts, do so [[self parentContext] MR_saveWithOptions:modifiedOptions completion:completion];

相似于递归调用,最终会调用根上下文,也就是保存到了数据库。

可是在这以前有个逻辑想到重要。也就是保存的上下文该没有改变。若是被肯定是没有改变的,那就不会中保存的逻辑。

__block BOOL hasChanges = NO; if ([self concurrencyType] == NSConfinementConcurrencyType) { hasChanges = [self hasChanges]; } else { [self performBlockAndWait:^{ hasChanges = [self hasChanges]; }]; } if (!hasChanges) { MRLogVerbose(@"NO CHANGES IN ** %@ ** CONTEXT - NOT SAVING", [self MR_workingName]); if (completion) { dispatch_async(dispatch_get_main_queue(), ^{ completion(NO, nil); }); } return; }

最后来一段有问题的代码。

// 在默认的上下文中建立实体 Person *person = [Person MR_createEntity]; // 改变person,引发上下文的改变 person.name = @"test"; person.age = @(100); [[NSManagedObjectContext MR_defaultContext] MR_saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) { } completion:^(BOOL contextDidSave, NSError * _Nullable error) { }];

这段代码不会保存成功。

由于在MR_saveWithBlock建立一个继承自上下文的心的localContext。然而person所作的改变是在默认上下文中,也便是localContext的父上下文。判断是否改变是根据localContext来判断的,结果就是hasChanges为NO。最终致使保存不成功。

那么改变一下就能够了。也便是咱们本身来控制保存。
以下:

// 在默认的上下文中建立实体 Person *person = [Person MR_createEntity]; // 改变person,引发上下文的改变 person.name = @"test"; person.age = @(100); [[NSManagedObjectContext MR_defaultContext] MR_saveWithOptions:MRSaveParentContexts completion:^(BOOL contextDidSave, NSError * _Nullable error) { }];

总结:

多看官方文档,多看三方库wiki,多总结。

养成有耐心的习惯。勿急躁。



 
 
 
参考连接:
 
1.http://www.cnblogs.com/YouXianMing/p/3597808.html
2.http://www.jianshu.com/p/590c6db9cdd6
3.http://www.aichengxu.com/view/2482547
相关文章
相关标签/搜索