IOS MagicRecord 详解

刚开始接触IOS不久,尝试着翻译一些博客,积累技术,与你们共享。 html

本篇内容讲解的是MagicRecord的使用,是对CoreData的深度封装,原文地址: ios

http://www.raywenderlich.com/56879/magicalrecord-tutorial-ios 欢迎你们指正,谢谢! git

CoreData做为Mac OS 和IOS开发数据持久化和用户数据检索的不可缺乏的一部分已经好几年了。为了使API对开发者更容易使用,也为了App的总体化,苹果也在不间断的更新CoreData的API。 github

也就是说,即便对于一个精通IOS开发的人CoreData依旧使用起来很困难。即便你会使用CoreData,天天重复性枯燥的使用CoreData也会变得很笨重,MagicalPanda建立的一个第三方库为这种工做带来了好消息。MagicalRecord 致力于更快捷和容易的使用CoreData。 数据库

MagicalRecord 使用方便,特别流行。正如做者所说,MagicalRecord 致力于使CoreData的代码更简洁,更简单的获取数据,而且使用最优化的操做。他是怎么作到的呢?它提供了方便的方法,包含了CoreData使用的查询更新等的公用模板。它的设计受到了Ruby on Rails'sActiveRecord 持续性系统的影响。
app

但这些理论已经足够了,请跟着这篇教程来见证MagicalRecord怎样应用。在这篇教程里,你将会建立一个app,该App将记录你所喜好啤酒的一个踪影。它将容许你作到如下几个事情:

1.添加你所喜好的一种啤酒。 ide

2.评价该种啤酒。
工具

3.为该种啤酒添加一个笔记。 性能

4.记录该种啤酒的照片----若是你有太多有意义的记录 学习

入门

      要学习本教程,你首先要对 Core Data有基本的理解,只须要了解基础的 Core Data教程,不须要有任何高级的了解。

      若是你一点也不了解CoreData,你最好先了解下 introductory Core Data tutorial,而后再继续阅读本教程。

      首先,请先下载Starter Project,若是已经下载好了,请继续,把工程跑起来,你就能够看到结果。


这是一个带有添加按钮、tableView和搜索栏(下拉table会出现)、能够按照评价或者字母排序的Segment Controllernavigation controller 。若是你点击了增长按钮,你讲进入而且查看到啤酒的详尽信息,若是你尝试进入其余页面,此信息还未保存。

如今咱们看一下代码,在工程的Navigator 里面你将看到:

1.此工程下的全部的ViewController

2.一个ImageSaver 工具类

3.稍后用来初始化数据的图片。

4.一个有一些被UI使用的图片的Images.xcassets库

5.AMRating ---一个用来评价控制的第三方库

6.最后是MagicalRecord

当你看代码的时候,你可能注意到了没有 Core Data模型,在AppDelegate.m里面也不包含任何启动core data的代码。在启动的时候看不到CoreData对任何工程来讲都是最完美的方案,只要记住咱们就是在使用CoreData的路上。

使用MagicalRecord开发

           在工程的Navigator栏里面展开MagicalRecord 文件夹,在这个文件夹里面你将会看见Categories 和Core文件夹以及CoreData+MagicalRecord.h文件。展开Categories 文件夹打开 NSManagedObjectModel+MagicalRecord.h.文件你将会发现头文件里面的方法都以 MR_做为前缀。事实上若是你看完了Categories 文件夹里面全部的文件你就会注意到全部的方法都是以MR_做为前缀的。我不知道你为前缀的方式。
     在工程的Navigator栏里面展开Supporting Files文件夹打开BeerTracker-Prefix.pch文件,该文件是工程的预编译文件,在文件里面增长以下代码:

[html]  view plain copy
  1. <span style="font-family:Times New Roman;font-size:14px;">#define MR_SHORTHAND  
  2. #import “CoreData+MagicalRecord.h”</span>  
 
 这两行代码使得MagicalRecord 在你的工程里面起做用。 
1.MR_SHORTHAND 告诉MagicalRecord 你不想在任何的MagicalRecord方法前加MR_前缀。你能够查看MagicalRecord+ShorthandSupport.m文件来查看这句话是怎么起做用的。因为这个已经超出了咱们本教程讨论的范围,咱们将不在这里讨论它。
2.经过导入CoreData+MagicalRecord.h文件,你能够在你的工程里访问MagicalRecord 的任何一个API,而不用在每个你须要用到该API的头文件里面导入。
注意:若是你想在你本身的工程里加MagicalRecord ,在 GitHub page上有一些小贴士。你也能够按照我在项目里面          MagicalRecord 的方式来添加。
1.从GitHub上面下载MagicalRecord 项目。
2.拖拽MagicalRecord 目录到你的XCode工程。
3.确保CoreData.framework 包被添加进了你的工程设置里面(Build Phases\Link Binary 
4.在你工程预编译头文件#ifdef __OBJC__代码的下面添加以下代码:
[html]  view plain copy
  1. <span style="font-family:Times New Roman;font-size:14px;">#define MR_SHORTHAND  
  2. #import “CoreData+MagicalRecord.h”</span>  

啤酒模型

为了记录你所喜好啤酒的踪影,你将须要一个模型,由于你不能寄但愿于本身记住他们,对吧?从Xcode 的菜单栏选中File\New\File…,在列表的左边选中Core Data 而且从选项里面选中Data Model。

把文件命名为 BeerModel.xcdatamodeld ,放到BeerTracker 文件夹里面。

在工程的导航栏里面选中刚建立的文件 BeerModel.xcdatamodeld来编辑。增长一个名字为Beer的实体(entity ),增长一个类型为String名字为Name的属性。

 
再增长一个名字为BeerDetails的实体,这个实体将记录啤酒的详细信息。例如:用户评价、笔记和在哪里找到图片。用相应的类型给BeerDetails增长下列的属性。

  • Attribute: image Type: String
  • Attribute: note Type: String
  • Attribute: rating Type: Integer 16


下一步你将建立这两个实体之间的关系(RelationShip),让Beer 实体可以知道那个BeerDetails实例隶属于本身。在BeerDetails实体下面建立一个新的关系(relationship ),命名为“beer”,以Beer为目的(destination) ,经过选择目标“beer”实体,你将创建这两个实体之间的第一部分关系。


经过选择Beer实体来完成创建这个关系。增长一个命名为beerDetails的关系,以BeerDetails为目的(destination)与上面的Beer相对应。如今你的Beer 实体将有一个BeerDetails 实体来对应本身。


下一步是建立类文件来展现实体,这一步你将用到XCode,可是因为Xcode操做的一些怪癖,你须要细心些。

首先在XCode工程的导航栏里面经过选中Core Data模型来编辑它。确保你在ENTITIES 面板里面高亮了Beer实体-------而不是BeerDetails 实体

下一步是在XCode的菜单栏里面执行Editor\Create NSManagedObject Subclass… 检查BeerModel,而后选择下一步。而后管理上面的实体,选择Beer和BeerDetail的复选框,若是没有被默认选中,双击确认Beer 是高亮的。点击下一步,而且建立,而后就会生成和上面建立的Beer 和BeerDetails 实体相对应的两个类。留意一下新建的这两个类,你就会发现每一个类都有和你刚刚定义的实体属性相对应的属性。

    特别留意下,检查下表明两个实体关系的属性,你就会发现Beer类拥有 BeerDetails * 类型的一个变量beerDetails ,你还将看到BeerDetails 类有一个NSManagedObject*类型的命名为beer的变量,为何不是 Beer * 类型的属性呢?这是由于在Xcode里面“Create NSManagedObject Subclass”命令的一个限制。这对该工程没有什么影响,所以忽略它吧。

注意:被好奇心折磨着,老是考虑为何不建立一个 Beer * 类型的变量,看起来,这貌似是Xcode里面CreateNSManagedObjectSubclass命令的限制。自从Xcode有了Core Data Model,这个命令就应该合乎情理的推断出在彼此的关系里,每个类的属性都应该包含其余类的类型。然而这个命令不止用来生成类的属性,也被用来生成用来定义这些类型的类的名称,而这个命令并无智能到来吧两件事情都处理好。一个解决方案是简单的调整一下生成代码,给肯定的类设置属性。另外一个解决方案是让XCode少作一些事,若是你在生成类代码以前已经很明确的在Data Model Inspector里面定义了类名,XCode将生成正确的类属性。

若是你很好奇Xcode怎么从数据模型生成代码,你能够查看mogenerator

如今你已经建立了Object数据类。是时候初始化 Core Data堆栈了。打开AppDelegate.m文件,在application:didFinishLaunchingWithOptions:方法里面,在return代码前加上下面内容:

[objc]  view plain copy
  1. // Setup CoreData with MagicalRecord  
  2. // Step 1. Setup Core Data Stack with Magical Record  
  3. // Step 2. Relax. Why not have a beer? Surely all this talk of beer is making you thirsty…  
  4. [MagicalRecord setupCoreDataStackWithStoreNamed:@"BeerModel"];  
若是你之前建立过Core Data类型的Xcode工程,你极可能知道在AppDelegate 里面初始化Core Data须要多少行代码。而使用MagicalRecord你只须要一行代码。

MagicalRecord提供了一些可选的方法来创建你的Core Data堆栈,能够经过下面的步骤:

1.你的后备存储类型。

2.你是否须要自动迁移。

3.你的Core Data 模型的名字。

若是你的模型文件和你的工程有同样的基本名称,(好比模型文件的名字是BeerTracker.xcdatamodeld,工程名字是:BeerTracker)

而后你就可使用MagicalRecord的下列3个方法,setupCoreDataStacksetupCoreDataStackWithInMemoryStore,或者setupAutoMigratingCoreDataStack.

使用名字中有AutoMigrating的方法,你能够改变你的模型,而且可能自动迁移你的存储,MagicalRecord 将帮你操纵这些。一般状况下Core Data则须要你手动添加代码来操做你对模型的一些改变。

调制Beer实体

如今你的Core Data模型堆栈已经建立起来了,你能够向list里面添加Beer对象了。打开BeerViewController.h文件,在@class AMRatingControl后面加:

[objc]  view plain copy
  1. @class  Beer;  
而后在 @interface后面添加一个Beer属性:

[objc]  view plain copy
  1. @property (nonatomicstrongBeer *beer;  

而后切换到 BeerViewController.m ,而后在顶部导入 Beer.h 和BeerDetails.h

[objc]  view plain copy
  1. #import "Beer.h"  
  2. #import "BeerDetails.h"  
viewDidLoad 里面加入如下代码:

[objc]  view plain copy
  1. - (void)viewDidLoad {  
  2.     // 1. If there is no beer, create new Beer  
  3.     if (!self.beer) {  
  4.         self.beer = [Beer createEntity];  
  5.     }  
  6.     // 2. If there are no beer details, create new BeerDetails  
  7.     if (!self.beer.beerDetails) {  
  8.         self.beer.beerDetails = [BeerDetails createEntity];  
  9.     }  
  10.     // View setup  
  11.     // 3. Set the title, name, note field and rating of the beer  
  12.     self.title = self.beer.name ? self.beer.name : @"New Beer";  
  13.     self.beerNameField.text = self.beer.name;  
  14.     self.beerNotesView.text = self.beer.beerDetails.note;  
  15.     self.ratingControl.rating = [self.beer.beerDetails.rating integerValue];  
  16.     [self.cellOne addSubview:self.ratingControl];  
  17.    
  18.     // 4. If there is an image path in the details, show it.  
  19.     if ([self.beer.beerDetails.image length] > 0) {  
  20.         // Image setup  
  21.         NSData *imgData = [NSData dataWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:self.beer.beerDetails.image]];  
  22.          [self setImageForBeer:[UIImage imageWithData:imgData]];  
  23.      }  
  24. }  
故障检测:若是你把上面的代码贴到你的工程里面报错了,按快捷键 Shift+Command+K  Clean你的工程。
BeerViewController 加载时,那是由于你有:

1.或者选择beer或者。。。

2.从MasterViewController 选择添加按钮

当视图加载好后,你将作如下事情:

1.检查beer 实例是否加载好,若没有,这意味你将新建一个Beer

2.若是beer没有任何的BeerDetails,建立一个BeerDetails对象。

3.为了创建视图,抓取beer的名字评价及笔记内容,若是beer没有名字(在Beer的新实例里面,它将给名字为“New Beer”

4.若是Beer包含了一个图片路径,将加载该图片到UIImageView里面。
为了编辑和添加新的beers,还有一些事情须要你创建,首先你须要可以编辑和添加名字,像以下方式编辑textFieldDidEndEditing:

[objc]  view plain copy
  1. - (void)textFieldDidEndEditing:(UITextField *)textField {  
  2.     if ([textField.text length] > 0) {  
  3.         self.title = textField.text;  
  4.         self.beer.name = textField.text;  
  5.     }  
  6. }  

如今你将结束添加名字,而后你就能够把 beer的名字在文本框内输入为任何内容,只要不空.

为了薄脆笔记内容到 beer的笔记值里面,找到textViewDidEndEditing:方法,向以下编辑

[objc]  view plain copy
  1. - (void)textViewDidEndEditing:(UITextView *)textView {  
  2.     [textView resignFirstResponder];  
  3.     if ([textView.text length] > 0) {  
  4.         self.beer.beerDetails.note = textView.text;  
  5.     }  
  6. }  

下一步确保用户在 View Controller 里面改变评价,beer的评价会自动更新.在updateRating里面添加:

[objc]  view plain copy
  1. - (void)updateRating {  
  2.     self.beer.beerDetails.rating = @(self.ratingControl.rating);  
  3. }  
当用户在详情页面点击 UIImageView, 将容许他们添加或者编辑一张图片.一个UIActionSheet 将会展现出来
,它将容许用户下哦那个相机胶卷选择照片,或者拍一张新的照片.若是用户想拍照片,你得确保照片已经保存在磁盘上了.不是吧照片保存在 Core Data里面(这将致使性能问题),而是保存在用户的文件目录里面.你只需把图片路径保存在CoreData里面.

经过实现UIImagePickerControllerDelegate 方法,在照片和图库之间管理互动,你须要在UIImagePickerController里面实现BeerViewController 委派,用来在故事板里面操纵视图.找到imagePickerController:didFinishPickingMediaWithinfo,添加以下代码:

[objc]  view plain copy
  1. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {  
  2.     // 1. Grab image and save to disk  
  3.     UIImage *image = info[UIImagePickerControllerOriginalImage];      
  4.     // 2. Remove old image if present  
  5.     if (self.beer.beerDetails.image) {  
  6.         [ImageSaver deleteImageAtPath:self.beer.beerDetails.image];  
  7.     }  
  8.     // 3. Save the image  
  9.     if ([ImageSaver saveImageToDisk:image andToBeer:self.beer]) {  
  10.         [self setImageForBeer:image];  
  11.     }  
  12.     [picker dismissViewControllerAnimated:YES completion:nil];  
  13. }  
下面讲解下上面的代码:

1.imagePickerController:didFinishPickingMediaWithInfo:间接的传递了用户选择图片的一个参数,,图片是在信息字典里面的,key是UIImagePickerControllerOriginalImage
2.若是Beer图像已经包含了一个图片,程序将把它从硬盘删除.所以你并不会把用户的存储占满.

3.接着新的图片被保存到硬盘,路径被添加到了BeerDetails 的图片属性里面.打开  ImageSaver 找到saveImageToDisk:andToBeer 看一下  是什么样子.一旦图片被成功保存,将会在UIImageView里面展示出来

4.选择器消失.


为了使上面的修改器做用,你将修改ImageSaver ,如今你已经建立了Beer 类,你能够取消import行和设置图片路径行的注释.打开ImageSaver.m,修改导入语句以下:

[objc]  view plain copy
  1. #import "ImageSaver.h"  
  2. #import "Beer.h"  
  3. #import "BeerDetails.h"  
如今你须要取消有if语句这行的注释
[objc]  view plain copy
  1. if ([imgData writeToFile:jpgPath atomically:YES]) {  
  2.         beer.beerDetails.image = path;  
  3. }  
ImageSaver 类已经作好准备来接受图片了.而且保存图片到手机的文件目录里面.保存路径到BeerDetails 

从这里开始用户有两个选择,取消或完成,当你建立一个视图,Beer实体就被建立了,而后插入到managedObjectContext里面。取消则会删除Beer 对象。找到cancelAdd,加入以下代码;

[objc]  view plain copy
  1. - (void)cancelAdd {  
  2.     [self.beer deleteEntity];  
  3.     [self.navigationController popViewControllerAnimated:YES];  
  4. }  
MagicalRecord 提供了一个好的方法来删除实体---从MagicalRecord 里面移出该实体。删除以后用户将返回到beers的主列表。

若是用户选择完成,将保存beer 并返回到主列表。找到addNewBeer,它将简单的pop出view controller,返回到列表。当视图消失时将调用viewWillDisapper,而后将轮流调用saveContext

如今saveContext 是空的,所以你须要添加代码来保存新的实体。向saveContext:添加以下代码:

[objc]  view plain copy
  1. - (void)saveContext {  
  2.     [[NSManagedObjectContext defaultContext] saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {  
  3.         if (success) {  
  4.             NSLog(@"You successfully saved your context.");  
  5.         } else if (error) {  
  6.             NSLog(@"Error saving context: %@", error.description);  
  7.         }  
  8.     }];  
  9. }  

接下来,还有几行代码要作,在  In  AppDelegate.m , 里面用 MagicalRecord创建  Core Data堆栈,这将建立一个app全局可用的默认的managedObjectContext  对象。当你建立了 Beer 和BeerDetails 实体,他们就被插入到defaultContext 里面了。MagicalRecord 容许你保存任何你多是使用saveToPersistentStoreWithCompletion: 的managedObjectContext 。若保存失败完成的块里面讲给你一个NSError。这里你已经添加了一个简单的if/else块来打印出保存defaultContext时发生的问题。

你想测试一下结果吗?继续运行你的app,选择+按钮,填满内容,选择完成。


这样作以后,在列表的顶部你没有发现新建的Beer,不要急,没问题,你将学会怎样调整问题。你可能发现调试器里面有遗传信息。与你所说的相反,你成功了!


  

Magical调试器

当app启动时,在  Core Data 堆栈启动期间 MagicalRecord  打印出信息,这说明Core Data 正在启动。而后建立了defaultContext. 对象。这和咱们前文提到的你保存beer 对象的是同一个defaultContext
当你用MagicalRecord选择完成或者保存时,又会出现一系列Log,当你保存时你将看到以下log信息:
一、defaultContext 保存到主线程。
二、任何上下文的父类都将保存,以标志1设计。
三、最后的log显示MagicalRecord 知道两个对象(Beer 和BeerDetail)都将保存,而且被成功保存。
MagicalRecord 为你打印出了不少log,若是你有问题,或者有些事情的表现出乎你的意料,你应该检查日志,来得到有用信息。
注意:
虽然我不推荐这莫作,若是你必定想看到发生什么状况当取消掉MagicalRecord 的日志打印。你能够在MagicalRecord.h, 的17行吧:
[objc]  view plain copy
  1. #define MR_ENABLE_ACTIVE_RECORD_LOGGING 1  

改为

[objc]  view plain copy
  1. #define MR_ENABLE_ACTIVE_RECORD_LOGGING 0  

Noch ein Bier, bitte(不会翻译)

若是应用程序值得所作的工做,你将须要看到你所添加的 beers 。打开MasterViewController.m在顶部导入 Beer.h,和 BeerDetails.h
为了获得全部保存在 Core Data的Beer,你须要作一些事,viewWillAppear:,里面,在调用 reloadData.以前添加fetch方法。
[objc]  view plain copy
  1. - (void)viewWillAppear:(BOOL)animated {  
  2.     [super viewWillAppear:animated];  
  3.     // Check if the user's sort preference has been saved.  
  4.     ...  
  5.     [self fetchAllBeers];  
  6.     [self.tableView reloadData];  
  7. }  
当视图第一次加载时,或者从查看或添加 beer返回时,将获取和加载全部的beers 到列表中。
找到fetchAllBeers, 加入下列代码:
[objc]  view plain copy
  1. - (void)fetchAllBeers {  
  2.     // 1. Get the sort key  
  3.     NSString *sortKey = [[NSUserDefaults standardUserDefaults] objectForKey:WB_SORT_KEY];  
  4.     // 2. Determine if it is ascending  
  5.     BOOL ascending = [sortKey isEqualToString:SORT_KEY_RATING] ? NO : YES;  
  6.     // 3. Fetch entities with MagicalRecord  
  7.     self.beers = [[Beer findAllSortedBy:sortKey ascending:ascending] mutableCopy];  
  8. }  
MasterViewController 容许用户经过评价来排序Beer---从5星到一星,或经过字母排序(A-Z)排序,当App第一次启动时,建立一个NSUserDefault 来经过评价排序,而且做为默认创建起来。在这个方法里面你作以下事情:
一、检索在NSUserDefault 保存的用来排序的key值。
二、若是排序key是”rating“,上升的变量设为否,若是是字幕的变量为是。
三、进行提取
没错这里作的就这莫多。
再来一次,你在使用MagicalRecord 的方法和CoreData交互,findAllSortedBy:ascending 只是用MagicalRecord 执行查找Core Data 实体中众多方法中的一种,其余的还有(注意:以后你将用到):
  • findAllInContext: –找到上下文提供的全部类型的实体 
  • findAll – 找到如今线程上下文对象的全部实体
  • findAllSortedBy:ascending:inContext: –和以前使用的相似可是限制了提供的上下文对象
  • findAllWithPredicate: – 容许你传递谓语动词来搜寻实体。
  • findAllSortedBy:ascending:withPredicate:inContext: – 容许传递升序标志来排序,容许一个特别的上下文,还容许传递一个谓语动词来过滤结果集
其实还有许多优势,----看一下: NSManagedObject+MagicalFinders.m .

为了在单元里添加beer的名称和评价,找到configureCell:atIndex:,添加下列代码:
[objc]  view plain copy
  1. - (void)configureCell:(UITableViewCell*)cell atIndex:(NSIndexPath*)indexPath {  
  2.     // Get current Beer  
  3.     Beer *beer = self.beers[indexPath.row];  
  4.     cell.textLabel.text = beer.name;      
  5.     // Setup AMRatingControl  
  6.     AMRatingControl *ratingControl;  
  7.     if (![cell viewWithTag:20]) {  
  8.         ratingControl = [[AMRatingControl alloc] initWithLocation:CGPointMake(19010) emptyImage:[UIImage imageNamed:@"beermug-empty"] solidImage:[UIImage imageNamed:@"beermug-full"] andMaxRating:5];  
  9.         ratingControl.tag = 20;  
  10.         ratingControl.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;  
  11.         ratingControl.userInteractionEnabled = NO;  
  12.         [cell addSubview:ratingControl];  
  13.     } else {  
  14.         ratingControl = (AMRatingControl*)[cell viewWithTag:20];  
  15.     }  
  16.     // Put beer rating in cell  
  17.     ratingControl.rating = [beer.beerDetails.rating integerValue];  
  18. }  
如今,再找到  prepareForSegue:sender: , 在if语句里检查若是segue标识符是“editBeer,” 添加:
[objc]  view plain copy
  1. if ([[segue identifier] isEqualToString:@"editBeer"]) {  
  2.     NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];  
  3.     Beer *beer = self.beers[indexPath.row];  
  4.     upcoming.beer = beer;  
  5. }  
这将把Beer对象传递给 BeerViewController , 所以就能够展现beer的详细信息,容许编辑等。
继续把你的工程跑起来:
此次你将看到你以前添加的Beer和它的评价,你还能够选择Beer而且编辑信息,当你返回时,列表就更新了。


试着查看某一个Beer,不要编辑信息,返回主列表,看一下log信息你将看到:
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x8b6bfa0) NO CHANGES IN ** DEFAULT ** CONTEXT - NOT SAVING

当你离开详情页,你在viewWillDisappear: 里面所写的代码将保存默认上下文。然而若是没有变化,   MagicalRecord  识别到没有必要执行一个保存操做,所以它跳过了执行。这样作的好处就是,你没有必要考虑你是否应该保存,只须要尝试保存,让 MagicalRecord 给你指出就能够了。

完成触摸
这还有一些你想让app给用户作的事情,好比删除Beer,列出你喜好啤酒的列表,执行查询。
删除

 MasterViewController.m,里面找到tableView:commitEditingStyle:forRowAtIndexPath:, 方法添加以下代码:

[objc]  view plain copy
  1. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {  
  2.     if (editingStyle == UITableViewCellEditingStyleDelete) {  
  3.         Beer *beerToRemove = self.beers[indexPath.row];  
  4.         // Remove Image from local documents  
  5.         if (beerToRemove.beerDetails.image) {  
  6.             [ImageSaver deleteImageAtPath:beerToRemove.beerDetails.image];  
  7.         }  
  8.         // Deleting an Entity with MagicalRecord  
  9.         [beerToRemove deleteEntity];  
  10.         [self saveContext];  
  11.         [self.beers removeObjectAtIndex:indexPath.row];  
  12.         [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];  
  13.     }  
  14. }  
记住这里调用了saveContext .方法,你须要添加一些代码来确保删除经过,你已经作过一次了,你能指出怎么作吗?准备。。。开始!

添加以下代码来保存上下文

[objc]  view plain copy
  1. // Save ManagedObjectContext using MagicalRecord  
  2. [[NSManagedObjectContext defaultContext] saveToPersistentStoreAndWait];  
严格来讲你不必知道它何时结束,你可使用 MagicalRecord  的其余方式来保存 managedObjectContext

把App跑起来,删除一个Beer(使用传统的删除cell的方式)若是你重启App,beer就消失了。你作的很对,保存了咱们的更改。这意味着你已经正确的使用了 MagicalRecord。拍拍肩膀,放松下。

Demo数据

给用户一些初始化的数据,用户就会很容易明白怎么记录本身喜欢的啤酒,这样会更好。在 AppDelegate.m里面导入 Beer.hBeerDetails.h. 当创建 Core Data堆栈以后加入如下代码:

[objc]  view plain copy
  1. // Setup App with prefilled Beer items.  
  2. if (![[NSUserDefaults standardUserDefaults] objectForKey:@"MR_HasPrefilledBeers"]) {  
  3.     // Create Blond Ale  
  4.     Beer *blondAle = [Beer createEntity];  
  5.     blondAle.name  = @"Blond Ale";  
  6.     blondAle.beerDetails = [BeerDetails createEntity];  
  7.     blondAle.beerDetails.rating = @4;  
  8.     [ImageSaver saveImageToDisk:[UIImage imageNamed:@"blond.jpg"] andToBeer:blondAle];  
  9.    
  10.     // Create Wheat Beer  
  11.     Beer *wheatBeer = [Beer createEntity];  
  12.     wheatBeer.name  = @"Wheat Beer";  
  13.     wheatBeer.beerDetails = [BeerDetails createEntity];  
  14.     wheatBeer.beerDetails.rating = @2;  
  15.     [ImageSaver saveImageToDisk:[UIImage imageNamed:@"wheat.jpg"] andToBeer:wheatBeer];  
  16.    
  17.     // Create Pale Lager  
  18.     Beer *paleLager = [Beer createEntity];  
  19.     paleLager.name  = @"Pale Lager";  
  20.     paleLager.beerDetails = [BeerDetails createEntity];  
  21.     paleLager.beerDetails.rating = @3;  
  22.     [ImageSaver saveImageToDisk:[UIImage imageNamed:@"pale.jpg"] andToBeer:paleLager];  
  23.    
  24.     // Create Stout  
  25.     Beer *stout = [Beer createEntity];  
  26.     stout.name  = @"Stout Lager";  
  27.     stout.beerDetails = [BeerDetails createEntity];  
  28.     stout.beerDetails.rating = @5;  
  29.     [ImageSaver saveImageToDisk:[UIImage imageNamed:@"stout.jpg"] andToBeer:stout];  
  30.    
  31.     // Save Managed Object Context  
  32.     [[NSManagedObjectContext defaultContext] saveToPersistentStoreWithCompletion:nil];  
  33.    
  34.     // Set User Default to prevent another preload of data on startup.  
  35.     [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"MR_HasPrefilledBeers"];  
  36.     [[NSUserDefaults standardUserDefaults] synchronize];  
  37. }  
在本教程的开始你下载的启动ap,包含了4张灰白色的啤酒照片。在这里你只须要建立4种不一样的啤酒,而后保存它们。在 NSUserDefaults  里面保存个标志,确保应用里面预先填好数据模型当应用程序第一次启动的时候。

再次运行应用程序,如今你就能够看到全部的新啤酒了。尝试删除一个,而后从新运行Ap,而后删掉的那个就消失了。若是你想再一次看到那几个啤酒,从设备里面删除应用,从新运行应用就能够了。

搜索

如今你已经有超过一种的啤酒,能够测试一下搜索的功能,启动Ap已经包含了一个搜索栏,滑动到列表的头部就能够看到。你所须要作的仅仅是添加搜索啤酒的逻辑。

以前你使用过MagicalRecord 的一个方法来获取全部种类的啤酒。而后她仅仅简单的返回了全部品种的啤酒,如今你须要在全部啤酒里面检索特殊种类的啤酒。

要作到这一点你须要用到谓语动词。以前教程已经讲解了一个使用谓语动词检索的方法。----你能猜出来怎么作吗?逻辑代码在MasterViewController.m 的doSearch 方法里面。

[objc]  view plain copy
  1. - (void)doSearch {  
  2.     // 1. Get the text from the search bar.  
  3.     NSString *searchText = self.searchBar.text;  
  4.     // 2. Do a fetch on the beers that match Predicate criteria.  
  5.     // In this case, if the name contains the string  
  6.     self.beers = [[Beer findAllSortedBy:SORT_KEY_NAME ascending:YES withPredicate:[NSPredicate predicateWithFormat:@"name contains[c] %@", searchText] inContext:[NSManagedObjectContext defaultContext]] mutableCopy];  
  7.     // 3. Reload the table to show the query results.  
  8.     [self.tableView reloadData];  
  9. }  

再次运行Ap将啤酒列表页面向下拽,一直到出现搜索栏。在列表栏里面搜索(列表里有的和没有的),你获得想要的结果了吗?


从这以后干什么

希望,MagicalRecord 教程可以展现给你使用MagicalRecord 有多么的容易。学会这个样板真的颇有用。你在教程里面探索的原理可以帮你开发各类ap,来帮助用户记录他们喜欢的图片,笔记,和评价。多享受啊!
你能够下载完整的代码在这个连接,若是你卡在哪里这将颇有帮助。
若是你想进一步开发这个工程,下面是一些想法:
一、在MasterViewController 里面添加“还没建立啤酒”的提示。-----检查并使用MagicalRecord 里面的hasAtLeastOneEntity方法。
二、添加消息提示有多少啤酒匹配搜索条件,使用countOfEntitiesWithPredicate: 方法。
三、完善数据库重置功能-----查看MagicalRecord里面的truncateAll 方法。
四、若是有兴趣打开 MagicalRecordShorthand.h 阅读如下里面的方法是名称----大部分方法的名字都很容易理解,这个头文件应该会给你一些怎么使用MagicalRecord的一些更好的想法。
若是你对本教程有任何问题,或者有任何评论,请加入下面的讨论。


初次翻译,错误之处还请谅解.  完整例子代码见:
http://download.csdn.net/detail/yjw179/9397366
相关文章
相关标签/搜索