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
相關文章
相關標籤/搜索