Magical Record是用來操作Core Data的一個第三方工具,在介紹Magical Record 之前必須要先了解一下Core Data的基本概念
Core Data基本介紹
Core Data Stack
核心數(shù)據(jù)堆棧是由一個或多個與單個persistent store coordinator
關聯(lián)的managed object contexts
組成,而persistent store coordinator
是和一個或多個persistent stores
關聯(lián)在一起。堆棧包含了CoreData的所有組件查詢斗搞,創(chuàng)建蔑匣,操作managed objects
.
簡單來說包含了:
- 一個包含了記錄的
persistent store
.類似于數(shù)據(jù)庫 - 一個在本地數(shù)據(jù)和對象之間的
persistent object store
- 一個聚合了所有存儲的
persistent store coordinator
- 一個描述實體的
managed object model
- 一個容器包含
managed objects
的managed object context
容器
可能有點繞瞭稼,不過一看圖世界就清晰了
如下圖:

Managed Object
Managed Object是一個模型對象(模型-視圖-控制器的意義上)企孩,它代表了一個持久存儲的記錄。管理對象是實例NSManagedObject或子類NSManagedObject涣楷。
管理對象有一個實體的描述對象分唾,告訴它代表著什么實體的引用。以這種方式狮斗,NSManagedObject可以表示任何實體不需要每個實體的唯一的子類绽乔。如果要實現(xiàn)自定義行為,例如計算派生屬性值碳褒,或者為了實現(xiàn)驗證邏輯可以使用一個子類折砸。
還是來看圖:

Managed Object Model

Manage Context Object
Manage Context Object代表單個對象的空間,沙峻,在核心數(shù)據(jù)的應用程序睦授。管理對象上下文的一個實例的NSManagedObjectContext。它的主要職責是管理管理對象的集合摔寨。這些管理對象代表一個或多個持久存儲的一個內(nèi)部一致的看法去枷。上下文是在管理對象的生命周期核心作用。
上下文是在核心數(shù)據(jù)堆棧中的中心對象是复。這是你用它來創(chuàng)建和獲取管理對象和管理撤消和恢復操作的對象删顶。內(nèi)的給定范圍內(nèi),有至多一個被管理目標代表在永久存儲器的任何給定的記錄淑廊。

上下文被連接到一個父對象存儲逗余。這通常是一個持久存儲協(xié)調(diào),但可能是另一個管理對象上下文季惩。當你獲取對象录粱,上下文要求其父對象存儲返回那些符合提取請求的對象腻格。您對管理對象的修改,直到您保存的背景下不被提交到父store啥繁。
在某些應用中荒叶,你可能想保持獨立組來管理對象和編輯這些對象的; 或者你可能需要執(zhí)行使用一個上下文,同時允許用戶與另一個對象交互的后臺操作
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;
每次調(diào)用Core Data
的堆棧的實例嘶居,提供給了這些實例的getter,setter方法罪帖。這些實例被MagicalRecord
很好的管理,被識別為默認方式邮屁。
當通過DEBUG
模式標識使用SQLite
數(shù)據(jù)庫整袁,不創(chuàng)建新的model
版本來改變model
將會引起MagicalRecord
自動的刪除老的數(shù)據(jù)庫并且自動的創(chuàng)建一個新的。這樣可以節(jié)約很多時間--不需要每次都卸載重裝app來讓data model
改變佑吝,確保你的app不是用的DEBUG模式:當刪除app數(shù)據(jù)的時候不告訴用戶真的是一種很糟糕的方式
在你的app退出之前坐昙,你應該調(diào)用類方法+cleanUp
[MagicalRecord cleanUp];
這將會清理MagicalRecord
,比如自定義的錯誤處理,讓通過MagicalRecord
創(chuàng)建的Core Data
堆棧為nil
.
使用Managed Object Contexts
創(chuàng)建新的上下文
一些簡單的類方法用來幫助快速的你創(chuàng)建新的上下文
- [NSManagedObjectContext MR_newMainQueueContext]:
- [NSManagedObjectContext MR_newPrivateQueueContext]:
- [NSManagedObjectContext MR_newContextWithStoreCoordinator:…]: 允許你具體化
persistent store coordinator
為新的上下文芋忿,有一個NSPrivateQueueConcurrencyType
- [NSManagedObjectContext MR_newContextWithStoreCoordinator:…]: 允許你具體化
默認上下文
當使用CoreData
,你將不斷的和兩個主要的對象打交道炸客,NSManagedObject
和 NSManagedObjectContext.
MagicalRecord
提高了一個簡單的類方法來獲取默認的NSManagedObjectContext
,這個上下文貫穿了你的app始終,這個上下文的操作會在在主線程中進行戈钢,并且對于單線程的app比較適合痹仙。
通過如下方式訪問到默認的上下文:
NSManagedObjectContext *defaultContext = [NSManagedObjectContext MR_defaultContext];
這個上下文將在MagicalRecord
任何使用了上下文的方法中使用,但是沒有提供一個具體的NSManagedObjectContext
參數(shù)殉了。
如果你需要創(chuàng)建一個不再主線程中使員工的上下文开仰,使用:
NSManagedObjectContext *myNewContext = [NSManagedObjectContext MR_newContext];
這種方式將會創(chuàng)建一個和default context
有相同的對象和persistent store
.安全在其他線程使用。它將會默認的將default context
作為它的父上下文宣渗。
如果你想默認讓myNewContext
實例化所有的fetch request
.使用類方法的方式
[NSManagedObjectContext MR_setDefaultContext:myNewContext];
注意:高度建議
default context
使用類型為NSMainQueueConcurrencyType
的上下文來創(chuàng)建并設置在主線程抖所。
在后臺線程中執(zhí)行
MagicalRecord
提供了方法來設置,協(xié)調(diào)上下文在后臺線程中使用痕囱。后臺保存操作受到了UIView
動畫使用Block
的方式田轧,但也存在了一些不同
在你對實體進行改變了的block,絕對不在主線程中執(zhí)行
單個的
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
已經(jīng)做了稽莉。并且應該更新瀑志,因為是在其他線程里面改變進行的。
當執(zhí)行完了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
在主線程中被調(diào)用污秆,為了UI更新更安全劈猪。
創(chuàng)建實體
在默認的上下文中插入一個實體,如下:
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
大多數(shù)方法是返回一個NSArray
數(shù)組良拼。
舉例战得,如果你有一個person
實體和department
實體關聯(lián),你可以查詢所有的person
實體從persistent store
通過如下方式實現(xiàn):
NSArray *people = [Person MR_findAll];
傳入一個具體的參數(shù)返回一個排序后的數(shù)組:
NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName" ascending:YES];
傳入多個具體的參數(shù)返回一個排序后的數(shù)組
NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName,FirstName" ascending:YES];
傳入多個不同參數(shù)值得到排序結果庸推,如果你不提供任何一個參數(shù)的默認值常侦,就會默認使用你在model中的設置。
NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName:NO,FirstName"
ascending:YES];
// OR
NSArray *peopleSorted = [Person MR_findAllSortedBy:@"LastName,FirstName:YES"
ascending:NO];
如果你有一種唯一從數(shù)據(jù)庫中查詢單個對象的方法(比如作為唯一屬性)贬媒,你可以通過下面的方法:
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];
關于每一行的調(diào)用, NSFetchRequest
和 NSSortDescriptor
作為排序的標配掖蛤。
自定有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];
查詢實體數(shù)量
可以執(zhí)行所有實體類型輸血量在persistent store
NSNumber *count = [Person MR_numberOfEntities];
或者基于查詢的數(shù)量
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:
,方法參數(shù)允許具體使用哪一個上下文查詢:
NSArray *peopleFromAnotherContext = [Person MR_findAllInContext:someOtherContext];
Person *personFromContext = [Person MR_findFirstByAttribute:@"lastName"
withValue:@"Gump"
inContext:someOtherContext];
NSUInteger count = [Person MR_numberOfEntitiesWithContext:someOtherContext];
存儲實體
什么時候應該保存
總的來說杀捻,當數(shù)據(jù)發(fā)生改變的時候應該保存到persistent store(s)
.一些應用選擇在應用終止的時候才保存糖荒。然而诫舅,在大多數(shù)場景下是不需要的却舀。事實上枚尼,只在應用終止的時候保存成畦,會有數(shù)據(jù)丟失的風險苞俘。萬一你的應用崩潰了怎么辦幌甘?用戶將丟失他對數(shù)據(jù)所做的改變硼控。那樣的話是一種相當糟糕的體驗港柜,可以簡單的避免请契。
如果你覺得執(zhí)行保存話費了大量的時間,有幾件事情需要考慮:
- 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.將任務分解為多個小任務保存:比如大量數(shù)據(jù)的導入應被分解為小塊爽锥,沒有確切的原則來決定一次導入多少數(shù)據(jù),你需要測量你應用的性能比如通過蘋果的Instruments和tune.
解決長期運行中的保存
在iOS上
當應用在iOS上終止運行畔柔,有一個很小的機會去清理氯夷,保存數(shù)據(jù)到磁盤。如果你知道保存操作可能會花一段時間靶擦,最好的方式就是去申請一個額外的截止時間腮考。比如:
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;
}];
確保仔細的讀過[
- beginBackgroundTaskWithExpirationHandler:](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html#//apple_ref/occ/instm/UIApplication/beginBackgroundTaskWithExpirationHandler:),不合適或者不需要的延長應用的存活時間可能讓你的應用被拒雇毫。
使用模式
- 保存數(shù)據(jù)
- 查找實體
- 導入數(shù)據(jù)
- 線程安全
日志
MagicalRecord建立了在和Core Data交互的時候的日志。當錯誤發(fā)生的時候踩蔚,這些錯誤將會被捕獲棚放。并且將打印到控制臺。
日志被配置為輸出調(diào)試信息(MagicalRecordLoggingLevelDebug)
在deug編譯的時候默認的馅闽,將會輸出日志信息MagicalRecordLoggingLevelError
在realease下飘蚯。
日志通過[MagicalRecord setLoggingLevel:]
配置,使用下面的幾種預定義的日主等級福也。
- MagicalRecordLogLevelOff:不開始日志
- MagicalRecordLoggingLevelError:記錄所有錯誤
- MagicalRecordLoggingLevelWarn:記錄警告和錯誤
- MagicalRecordLoggingLevelInfo:記錄日志有用的信息孝冒,錯誤,警告
- MagicalRecordLoggingLevelDebug:所有調(diào)試信息
- MagicalRecordLoggingLevelVerbose:日志冗長的診斷信息拟杉,有用的信息,錯誤量承,警告
日志默認等級是 MagicalRecordLoggingLevelWarn
關閉日志
大多數(shù)人而言搬设,這個不需要,設置日志等級為MagicalRecordLogLevelOff將會保證不再打印日志信息
甚至當使用了MagicalRecordLogLevelOff撕捍,快速檢測檢查可能被調(diào)用無論何時日志被調(diào)用拿穴。如果想絕對的關閉日志。你可以定義如下忧风,當編譯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里面非常重要的類吃度。它有父上下文和子上下文的概念。經(jīng)過了漫長的爬坑贴硫,終于在蘋果官方文檔中找到了關于它詳細的介紹椿每。
這里只截取``parent store``這節(jié)來講
> ``Managed object contexts ``有一個父存儲,通過它來檢索數(shù)據(jù)英遭,提交改變
>
> 最開始在iOS5的之前间护,父存儲一直是``persistent store coordinator``。在iOS5之后挖诸。父存儲的類型可以是其他的``Managed object contexts ``汁尺。但是最終的根context必須是`persistent store coordinator``。協(xié)調(diào)者提高被管理的對象模型税灌,調(diào)用各種對數(shù)據(jù)庫的請求均函。
>
> 如果父存儲是一個``Managed object contexts ``亿虽。查詢,保存的操作是被父存儲來協(xié)調(diào)的而不是``persistent store coordinator``苞也。這種方式有兩個好處洛勉,
>
>* 1.在其他線程中執(zhí)行操作
>* 2.管理廢棄的編輯,比如監(jiān)視窗口如迟、view
> 第一種場景收毫,父上下文能夠通過不同的線程從子中獲得請求,
> * 重點部分:當在上下文中保存所做的改變的時候殷勘,改變只會被提交一次存儲此再,如果有子的上下文,改變將會推到他的父上下文玲销,改變不會直接保存到數(shù)據(jù)庫输拇,直到根上下文被保存才會保存到數(shù)據(jù)庫(根管理對象的上下文的父上下文為空)。除此之外贤斜,父上下文在保存之前不會從子中拉取數(shù)據(jù)的改變策吠。如果你想最后提交數(shù)據(jù)的改變,必須保存子上下文瘩绒,這樣就可以推到父上下文中猴抹。
## 測試代碼
上下文的創(chuàng)建時通過線程來控制,也就是上下文和線程相關锁荔。``[[NSThread currentThread] threadDictionary];``返回的字典就是處理數(shù)據(jù)方面的蟀给。
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))
{
// 創(chuàng)建新的上下文
threadContext = [self MR_contextWithParent:[NSManagedObjectContext MR_defaultContext]];
[threadDict setObject:threadContext forKey:kMagicalRecordManagedObjectContextKey];
[threadDict setObject:[NSNumber numberWithInteger:targetCacheVersionForContext]
forKey:kMagicalRecordManagedObjectContextCacheVersionKey];
}
return threadContext;
}
在配置的時候就會默認創(chuàng)建兩種上下文阳堕,一個根上下文跋理,和協(xié)調(diào)者直接通信的,一個是主線程相關的默認上下文恬总。默認上下文是根上下文的子薪介。
* 有必要說一說``MR_saveWithBlock``這個方法,自己在寫的時候就犯錯了越驻。
開看看實現(xiàn)
-
(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];
}];
}
是在當前的上下文中新建子然后通過子去保存汁政,注意這里的保存方法有個參數(shù)``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];
類似于遞歸調(diào)用记劈,最終會調(diào)用根上下文,也就是保存到了數(shù)據(jù)庫并巍。
但是在這之前有個邏輯想到重要目木。也就是保存的上下文該沒有改變。如果被確定是沒有改變的懊渡,那就不會中保存的邏輯刽射。
__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;
}
最后來一段有問題的代碼军拟。
// 在默認的上下文中創(chuàng)建實體
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``創(chuàng)建一個繼承自上下文的心的localContext懈息。然而person所做的改變是在默認上下文中,也即是localContext的父上下文摹恰。判斷是否改變是根據(jù)localContext來判斷的辫继,結果就是hasChanges為NO。最終導致保存不成功俗慈。
那么改變一下就可以了姑宽。也即是我們自己來控制保存。
如下:
// 在默認的上下文中創(chuàng)建實體
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,多總結酣溃。
養(yǎng)成有耐心的習慣示血。勿急躁。