MagicalRecord是一個簡化CoreData操作的工具庫,既然用到了這個庫幌缝,那就看一下源碼了解一下這個庫是怎么工作的,我閱讀的版本是2.3.2。
MagicalRecord的好處
- 清理我的Core Data相關(guān)代碼
- 支持清晰,簡單,一行代碼式的查詢
- 當(dāng)需要優(yōu)化請求時,仍然可以修改 NSFetchRequest
MagicalRecord源碼
MagicalRecord的初始化
我們使用CoreData通常是這樣的:
- 根據(jù)momd路徑創(chuàng)建托管對象模型
- 根據(jù)托管對象模型創(chuàng)建持久化存儲協(xié)調(diào)器
- 創(chuàng)建并關(guān)聯(lián)SQLite數(shù)據(jù)庫文件
- 創(chuàng)建管理對象上下文并指定持久化存儲協(xié)調(diào)器
// 創(chuàng)建托管對象模型红竭,并使用CoreData.momd路徑當(dāng)做初始化參數(shù)
NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"CoreData" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath];
// 根據(jù)托管對象模型創(chuàng)建持久化存儲協(xié)調(diào)器
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// 創(chuàng)建并關(guān)聯(lián)SQLite數(shù)據(jù)庫文件,如果已經(jīng)存在則不會重復(fù)創(chuàng)建
NSString *dataPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
dataPath = [dataPath stringByAppendingFormat:@"/%@.sqlite", @"CoreData"];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dataPath] options:nil error:nil];
// 創(chuàng)建上下文對象喘落,并發(fā)隊列設(shè)置為主隊列
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// 指定持久化存儲協(xié)調(diào)器
context.persistentStoreCoordinator = coordinator;
而使用MagicalRecord我們只需要調(diào)用[MagicalRecord setupCoreDataStack]
這個方法的具體實現(xiàn)是
+ (void) setupCoreDataStack
{
[self setupCoreDataStackWithStoreNamed:[self defaultStoreName]];
}
+ (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName
{
//判斷默認持久化存儲協(xié)調(diào)器是否存在
if ([NSPersistentStoreCoordinator MR_defaultStoreCoordinator] != nil) return;
//不存在就創(chuàng)建持久化存儲協(xié)調(diào)器
NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithSqliteStoreNamed:storeName];
//設(shè)置默認持久化存儲協(xié)調(diào)器
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:coordinator];
//創(chuàng)建NSManagedObjectContext
[NSManagedObjectContext MR_initializeDefaultContextWithCoordinator:coordinator];
}
創(chuàng)建持久化存儲協(xié)調(diào)器
+ (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName withOptions:(NSDictionary *)options
{
//獲取默認托管對象模型并創(chuàng)建持久化存儲協(xié)調(diào)器
NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
//添加持久化存儲到協(xié)調(diào)器
[psc MR_addSqliteStoreNamed:storeFileName withOptions:options];
return psc;
}
添加SQLite存儲
- (NSPersistentStore *) MR_addSqliteStoreNamed:(id)storeFileName configuration:(NSString *)configuration withOptions:(__autoreleasing NSDictionary *)options
{
//持久化存儲URL
NSURL *url = [storeFileName isKindOfClass:[NSURL class]] ? storeFileName : [NSPersistentStore MR_urlForStoreName:storeFileName];
NSError *error = nil;
//創(chuàng)建持久化存儲文件
[self MR_createPathToStoreFileIfNeccessary:url];
NSPersistentStore *store = [self addPersistentStoreWithType:NSSQLiteStoreType
configuration:configuration
URL:url
options:options
error:&error];
//數(shù)據(jù)庫不存在
if (!store)
{
/*
如果工程有DEBUG標記茵宪,則kMagicalRecordShouldDeleteStoreOnModelMismatch被設(shè)為YES
此時使用默認的SQLite數(shù)據(jù)存儲,不創(chuàng)建新的版本的數(shù)據(jù)模型而是直接改變數(shù)據(jù)模型本身的方式,將會刪除舊的存儲并自動創(chuàng)建一個新的瘦棋。
這會節(jié)省大量的時間 - 不再需要在改變數(shù)據(jù)模型后每次都重新卸載和安裝應(yīng)用
*/
if ([MagicalRecord shouldDeleteStoreOnModelMismatch])
{
BOOL isMigrationError = (([error code] == NSPersistentStoreIncompatibleVersionHashError) || ([error code] == NSMigrationMissingSourceModelError) || ([error code] == NSMigrationError));
if ([[error domain] isEqualToString:NSCocoaErrorDomain] && isMigrationError)
{
[[NSNotificationCenter defaultCenter] postNotificationName:kMagicalRecordPSCMismatchWillDeleteStore object:nil];
NSError * deleteStoreError;
// 無法打開數(shù)據(jù)庫就刪除
NSString *rawURL = [url absoluteString];
NSURL *shmSidecar = [NSURL URLWithString:[rawURL stringByAppendingString:@"-shm"]];
NSURL *walSidecar = [NSURL URLWithString:[rawURL stringByAppendingString:@"-wal"]];
[[NSFileManager defaultManager] removeItemAtURL:url error:&deleteStoreError];
[[NSFileManager defaultManager] removeItemAtURL:shmSidecar error:nil];
[[NSFileManager defaultManager] removeItemAtURL:walSidecar error:nil];
MRLogWarn(@"Removed incompatible model version: %@", [url lastPathComponent]);
if(deleteStoreError) {
[[NSNotificationCenter defaultCenter] postNotificationName:kMagicalRecordPSCMismatchCouldNotDeleteStore object:nil userInfo:@{@"Error":deleteStoreError}];
}
else {
[[NSNotificationCenter defaultCenter] postNotificationName:kMagicalRecordPSCMismatchDidDeleteStore object:nil];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kMagicalRecordPSCMismatchWillRecreateStore object:nil];
// 再次創(chuàng)建持久化存儲
store = [self addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:url
options:options
error:&error];
if (store)
{
[[NSNotificationCenter defaultCenter] postNotificationName:kMagicalRecordPSCMismatchDidRecreateStore object:nil];
error = nil;
}
else {
[[NSNotificationCenter defaultCenter] postNotificationName:kMagicalRecordPSCMismatchCouldNotRecreateStore object:nil userInfo:@{@"Error":error}];
}
}
}
[MagicalRecord handleErrors:error];
}
return store;
}
在平時開發(fā)過程中稀火,經(jīng)常會出現(xiàn)修改模型結(jié)構(gòu)的情況,需要把app卸載了然后重新安裝才能避免打不開數(shù)據(jù)庫導(dǎo)致崩潰的問題赌朋。MagicalRecord的處理方式是在DEBUG模式下將數(shù)據(jù)庫直接刪除重新創(chuàng)建凰狞,可以節(jié)省很多時間。
接著創(chuàng)建管理對象上下文
+ (void) MR_initializeDefaultContextWithCoordinator:(NSPersistentStoreCoordinator *)coordinator;
{
if (MagicalRecordDefaultContext == nil)
{
NSManagedObjectContext *rootContext = [self MR_contextWithStoreCoordinator:coordinator];
[self MR_setRootSavingContext:rootContext];
NSManagedObjectContext *defaultContext = [self MR_newMainQueueContext];
[self MR_setDefaultContext:defaultContext];
[defaultContext setParentContext:rootContext];
}
}
Core Data有很多種設(shè)置方式箕慧,常用有3種
兩個上下文服球,一個協(xié)調(diào)器
一個位于主線程的上下文,一個位于后臺線程的上下文颠焦,共用一個協(xié)調(diào)器斩熊。UI相關(guān)的操作在主線程的上下文上進行,后臺操作放入后臺線程執(zhí)行伐庭。當(dāng)后臺線程上下文保存后粉渠,通過監(jiān)聽NSManagedObjectContextDidSaveNotification
來更新主線程的上下文中的數(shù)據(jù)。把獲取和保存等操作放入后臺線程來避免阻塞主線程圾另。由于使用同一個協(xié)調(diào)器霸株,兩個上下文可以共用同一個行緩存,避免了一些對數(shù)據(jù)庫不必要的訪問集乔,提高了性能去件。但是由于共享一個協(xié)調(diào)器,所以在同一時間只有一個上下文能使用協(xié)調(diào)器扰路。并且要設(shè)置合適的合并策略來解決多個上下文都有更改是造成的沖突尤溜。兩個協(xié)調(diào)器
兩個獨立的協(xié)調(diào)器,共享同一個SQLite數(shù)據(jù)庫汗唱。創(chuàng)建一個主線程上下文宫莱,連接一個協(xié)調(diào)器,一個后臺線程上下文哩罪,連接一個協(xié)調(diào)器授霸。這樣的設(shè)置可以減少對協(xié)調(diào)器的競爭問題巡验,提供了更好的并發(fā)處理能力,因為SQLite支持多讀單寫碘耳,在不進行多個寫操作時显设,不會產(chǎn)生競爭問題。但是由于行緩存位于協(xié)調(diào)器層藏畅,所以行緩存不能共享敷硅。也就是說如果某一個上下文修改了數(shù)據(jù),另一個上下文需要獲取到這個數(shù)據(jù)需要下降到數(shù)據(jù)庫層愉阎。訪問數(shù)據(jù)庫相比于訪問內(nèi)存效率要低绞蹦。嵌套上下文
一個位于主線程的上下文,一個位于后臺線程的上下文榜旦,將后臺線程上下文直接與協(xié)調(diào)器相連幽七,主線程上下文的父上下文設(shè)置為后臺線程上下文。使用這種設(shè)置溅呢,主線程上下文的操作完全在內(nèi)存中進行澡屡,因為所有的操作只會被push到父上下文中,當(dāng)父上下文進行保存操作時咐旧,才會通過持久化存儲協(xié)調(diào)器訪問數(shù)據(jù)庫驶鹉,由于父上下文是一個私有隊列上下文,這些操作不會阻塞UI線程铣墨。
MagicalRecord使用的是嵌套上下文的設(shè)計室埋。原因可能是相比于其他兩種設(shè)計,嵌套上下文使用簡單伊约,管理方便姚淆,并且也能很好的分離UI操作和后臺數(shù)據(jù)操作,雖然性能不是最高的屡律,但是適用于大部分APP腌逢。
MagicalRecord保存
MagicalRecord提供了defaultContext讓我們在主線程處理有關(guān)UI的數(shù)據(jù)。如果我們想在后臺操作數(shù)據(jù)超埋,可以使用+ saveWithBlock:completion:
函數(shù)搏讶。這個函數(shù)用于更改實體的Block永遠不會在主線程執(zhí)行,并且提供一個rootContext的子context霍殴。當(dāng)Block中的操作執(zhí)行完畢窍蓝,會回調(diào)completion block,這個block在主線程中調(diào)用繁成,所以可以在此block里安全觸發(fā)UI更新。
+ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion;
{
//創(chuàng)建一個父上下文是rootContext的上下文淑玫,類型是NSPrivateQueueConcurrencyType
NSManagedObjectContext *savingContext = [NSManagedObjectContext MR_rootSavingContext];
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextWithParent:savingContext];
//子上下文通過performBlock回調(diào)到自己的線程中執(zhí)行block
[localContext performBlock:^{
[localContext MR_setWorkingName:NSStringFromSelector(_cmd)];
if (block) {
block(localContext);
}
//保存數(shù)據(jù)
[localContext MR_saveWithOptions:MRSaveParentContexts completion:completion];
}];
}
而要進行同步保存的話使用MR_saveToPersistentStoreAndWait
函數(shù)
- (void) MR_saveToPersistentStoreAndWait
{
[self MR_saveWithOptions:MRSaveParentContexts | MRSaveSynchronously completion:nil];
}
在保存選項中增加了同步保存的選項巾腕,如何進行同步和后臺的存儲面睛,讓我們看看保存數(shù)據(jù)的具體代碼
- (void) MR_saveWithOptions:(MRSaveOptions)saveOptions completion:(MRSaveCompletionHandler)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;
}
//父上下文是否應(yīng)該保存
BOOL shouldSaveParentContexts = ((saveOptions & MRSaveParentContexts) == MRSaveParentContexts);
//是否應(yīng)該同步保存
BOOL shouldSaveSynchronously = ((saveOptions & MRSaveSynchronously) == MRSaveSynchronously);
//是否應(yīng)該同步保存除了rootContext
BOOL shouldSaveSynchronouslyExceptRoot = ((saveOptions & MRSaveSynchronouslyExceptRootContext) == MRSaveSynchronouslyExceptRootContext);
//判斷是否同步保存
BOOL saveSynchronously = (shouldSaveSynchronously && !shouldSaveSynchronouslyExceptRoot) ||
(shouldSaveSynchronouslyExceptRoot && (self != [[self class] MR_rootSavingContext]));
//保存block
id saveBlock = ^{
MRLogInfo(@"→ Saving %@", [self MR_description]);
MRLogVerbose(@"→ Save Parents? %@", shouldSaveParentContexts ? @"YES" : @"NO");
MRLogVerbose(@"→ Save Synchronously? %@", saveSynchronously ? @"YES" : @"NO");
BOOL saveResult = NO;
NSError *error = nil;
@try
{
saveResult = [self save:&error];
}
@catch(NSException *exception)
{
MRLogError(@"Unable to perform save: %@", (id)[exception userInfo] ?: (id)[exception reason]);
}
@finally
{
[MagicalRecord handleErrors:error];
//判斷父上下文是否需要保存
if (saveResult && shouldSaveParentContexts && [self parentContext])
{
// 添加或移除同步選項
MRSaveOptions modifiedOptions = saveOptions;
if (saveSynchronously)
{
modifiedOptions |= MRSaveSynchronously;
}
else
{
modifiedOptions &= ~MRSaveSynchronously;
}
// 父上下文遞歸保存
[[self parentContext] MR_saveWithOptions:modifiedOptions completion:completion];
}
else
{
if (saveResult)
{
MRLogVerbose(@"→ Finished saving: %@", [self MR_description]);
}
if (completion)
{
//成功回調(diào)主線程
dispatch_async(dispatch_get_main_queue(), ^{
completion(saveResult, error);
});
}
}
}
};
if (saveSynchronously)
{
//通過performBlockAndWait:調(diào)度到自己的線程中保存并阻塞線程直到block處理結(jié)束
[self performBlockAndWait:saveBlock];
}
else
{
//調(diào)度到自己的線程中保存尊搬,不阻塞線程
[self performBlock:saveBlock];
}
}
保存數(shù)據(jù)時根據(jù)saveOptions決定是否同步以及父上下文是否保存叁鉴。如果同步,調(diào)用performBlockAndWait:
函數(shù)佛寿,否則調(diào)用performBlock:
函數(shù)幌墓。通過遞歸調(diào)用的方式進行父上下文的保存直到rootContext將數(shù)據(jù)通過 Persistent Store Coordinator存入數(shù)據(jù)庫。在其它線程保存完畢后冀泻,默認上下文如何更新數(shù)據(jù)呢常侣?在設(shè)置默認上下文時已經(jīng)監(jiān)聽了NSManagedObjectContextDidSaveNotification
事件。
if ((MagicalRecordDefaultContext != nil) && ([self MR_rootSavingContext] != nil)) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(rootContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:[self MR_rootSavingContext]];
}
+ (void)rootContextDidSave:(NSNotification *)notification
{
//判斷是否是rootContext存儲完畢的通知
if ([notification object] != [self MR_rootSavingContext])
{
return;
}
if ([NSThread isMainThread] == NO)
{
//不是主線程重新在主線程中調(diào)用rootContextDidSave:
dispatch_async(dispatch_get_main_queue(), ^{
[self rootContextDidSave:notification];
});
return;
}
for (NSManagedObject *object in [[notification userInfo] objectForKey:NSUpdatedObjectsKey])
{
[[[self MR_defaultContext] objectWithID:[object objectID]] willAccessValueForKey:nil];
}
[[self MR_defaultContext] mergeChangesFromContextDidSaveNotification:notification];
}
當(dāng)后臺線程操作完數(shù)據(jù)弹渔,默認上下文接到通知合并更改胳施。
MagicalRecord增刪改查
創(chuàng)建實體對象
+ (id) MR_createEntityInContext:(NSManagedObjectContext *)context
{
if ([self respondsToSelector:@selector(insertInManagedObjectContext:)] && context != nil)
{
//判斷是否實現(xiàn)了`insertInManagedObjectContext:`函數(shù),如果可以通過這個函數(shù)創(chuàng)建實體
id entity = [self performSelector:@selector(insertInManagedObjectContext:) withObject:context];
return entity;
}
else
{
//通過entityName生成NSEntityDescription
NSEntityDescription *entity = nil;
if (context == nil)
{
entity = [self MR_entityDescription];
}
else
{
entity = [self MR_entityDescriptionInContext:context];
}
if (entity == nil)
{
return nil;
}
//創(chuàng)建entity并返回
return [[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
}
}
刪除實體
- (BOOL) MR_deleteEntityInContext:(NSManagedObjectContext *)context
{
NSError *error = nil;
//跨上下文不能直接訪問實體對象肢专,通過實體對象的ID判斷當(dāng)前上下文中是否存在這個實體對象
NSManagedObject *entityInContext = [context existingObjectWithID:[self objectID] error:&error];
[MagicalRecord handleErrors:error];
//如果實體對象存在就刪除
if (entityInContext) {
[context deleteObject:entityInContext];
}
return YES;
}
刪除全部實體
+ (BOOL) MR_truncateAllInContext:(NSManagedObjectContext *)context
{
//創(chuàng)建獲取所有實體的請求
NSFetchRequest *request = [self MR_requestAllInContext:context];
//設(shè)置返回值為惰值
[request setReturnsObjectsAsFaults:YES];
//只獲取ObjectID
[request setIncludesPropertyValues:NO];
//查找要刪除的數(shù)據(jù)
NSArray *objectsToDelete = [self MR_executeFetchRequest:request inContext:context];
for (NSManagedObject *objectToDelete in objectsToDelete)
{
//刪除數(shù)據(jù)
[objectToDelete MR_deleteEntityInContext:context];
}
return YES;
}
刪除全部實體時舞肆,通過使用惰值和ObjectID減少對內(nèi)存的占用。也可以使用MR_deleteAllMatchingPredicate:inContext:
通過指定謂詞來刪除符合條件的數(shù)據(jù)博杖。
MagicalRecord的查找
+ MR_findAllInContext:context
+ MR_findAllSortedBy:ascending:inContext:
+ MR_findAllSortedBy:ascending:withPredicate:inContext:
+ MR_findAllWithPredicate:inContext:
這些查找方法都會調(diào)用MR_executeFetchRequest:inContext:
+ (NSArray *) MR_executeFetchRequest:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context
{
__block NSArray *results = nil;
[context performBlockAndWait:^{
NSError *error = nil;
results = [context executeFetchRequest:request error:&error];
if (results == nil)
{
[MagicalRecord handleErrors:error];
}
}];
return results;
}
最后都是通過executeFetchRequest:error:
函數(shù)返回查詢結(jié)果椿胯。
MagicalRecord還提供了短方法名,比如用 findAll 代替 MR_findAll剃根,調(diào)用[MagicalRecord enableShorthandMethods]
開啟哩盲。通過Runtime的方法交換和動態(tài)方法解析來實現(xiàn)。
+ (void)enableShorthandMethods
{
if (kMagicalRecordShorthandMethodsSwizzled == NO)
{
NSArray *classes = [self classesToSwizzle];
//將數(shù)組中的類的resolveClassMethod:和resolveInstanceMethod:方法替換
[classes enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
Class objectClass = (Class)object;
[self updateResolveMethodsForClass:objectClass];
}];
kMagicalRecordShorthandMethodsSwizzled = YES;
}
}
+ (NSArray *)classesToSwizzle
{
return @[ [NSManagedObject class],
[NSManagedObjectContext class],
[NSManagedObjectModel class],
[NSPersistentStore class],
[NSPersistentStoreCoordinator class] ];
}
+ (void)updateResolveMethodsForClass:(Class)objectClass
{
MRReplaceSelectorForTargetWithSourceImplementation(self, @selector(MR_resolveClassMethod:), objectClass, @selector(resolveClassMethod:));
MRReplaceSelectorForTargetWithSourceImplementation(self, @selector(MR_resolveInstanceMethod:), objectClass, @selector(resolveInstanceMethod:));
}
static void MRReplaceSelectorForTargetWithSourceImplementation(Class sourceClass, SEL sourceSelector, Class targetClass, SEL targetSelector)
{
// 獲取MR_resolveClassMethod:方法
Method sourceClassMethod = class_getClassMethod(sourceClass, sourceSelector);
// 獲取resolveInstanceMethod:方法
Method targetClassMethod = class_getClassMethod(targetClass, targetSelector);
// 獲取目標類的源類
Class targetMetaClass = objc_getMetaClass([NSStringFromClass(targetClass) cStringUsingEncoding:NSUTF8StringEncoding]);
//向目標源類中添加一個方法跟继,SEL為MR_resolveClassMethod:方法的SEL种冬,IMP為resolveInstanceMethod:的IMP
BOOL methodWasAdded = class_addMethod(targetMetaClass, sourceSelector,
method_getImplementation(targetClassMethod),
method_getTypeEncoding(targetClassMethod));
if (methodWasAdded)
{
//把目標源類中的resolveInstanceMethod:方法的IMP替換為MR_resolveClassMethod:方法的IMP
class_replaceMethod(targetMetaClass, targetSelector,
method_getImplementation(sourceClassMethod),
method_getTypeEncoding(sourceClassMethod));
}
}
static NSString *const kMagicalRecordCategoryPrefix = @"MR_";
+ (BOOL)MR_resolveClassMethod:(SEL)originalSelector
{
BOOL resolvedClassMethod = [self MR_resolveClassMethod:originalSelector];
if (!resolvedClassMethod)
{
// 如果原resolveClassMethod:實現(xiàn)無法解析SEL,向本類添加短方法名的實現(xiàn)
resolvedClassMethod = MRAddShortHandMethodForPrefixedClassMethod(self, originalSelector, kMagicalRecordCategoryPrefix);
}
// 返回YES重新發(fā)送消息舔糖,返回NO則進入消息轉(zhuǎn)發(fā)
return resolvedClassMethod;
}
+ (BOOL)MR_resolveInstanceMethod:(SEL)originalSelector
{
BOOL resolvedClassMethod = [self MR_resolveInstanceMethod:originalSelector];
if (!resolvedClassMethod)
{
// 同上
resolvedClassMethod = MRAddShorthandMethodForPrefixedInstanceMethod(self, originalSelector, kMagicalRecordCategoryPrefix);
}
return resolvedClassMethod;
}
static BOOL MRAddShorthandMethodForPrefixedInstanceMethod(Class objectClass, SEL originalSelector, NSString *prefix)
{
NSString *originalSelectorString = NSStringFromSelector(originalSelector);
// 根據(jù)名稱判斷是否添加實現(xiàn)
if ([originalSelectorString hasPrefix:prefix] == NO &&
([originalSelectorString hasPrefix:@"_"] || [originalSelectorString hasPrefix:@"init"]))
{
// 在短方法名前添加MR_
NSString *prefixedSelector = [prefix stringByAppendingString:originalSelectorString];
// 獲取帶MR_的方法
Method existingMethod = class_getInstanceMethod(objectClass, NSSelectorFromString(prefixedSelector));
if (existingMethod)
{
// 向類中添加SEL為短方法名娱两,IMP為帶MR_的方法實現(xiàn)
BOOL methodWasAdded = class_addMethod(objectClass,
originalSelector,
method_getImplementation(existingMethod),
method_getTypeEncoding(existingMethod));
// 返回添加結(jié)果
return methodWasAdded;
}
}
return NO;
}
static BOOL MRAddShortHandMethodForPrefixedClassMethod(Class objectClass, SEL originalSelector, NSString *prefix)
{
NSString *originalSelectorString = NSStringFromSelector(originalSelector);
// 根據(jù)名稱判斷是否添加實現(xiàn)
if ([originalSelectorString hasPrefix:prefix] == NO &&
[originalSelectorString hasSuffix:@"entityName"] == NO)
{
// 在短方法名前添加MR_
NSString *prefixedSelector = [prefix stringByAppendingString:originalSelectorString];
// 獲取帶MR_的方法
Method existingMethod = class_getClassMethod(objectClass, NSSelectorFromString(prefixedSelector));
if (existingMethod)
{
// 獲取本類的源類。
// 因為類對象的方法列表在源類中金吗,調(diào)用類方法時十兢,通過類對象指向源類的isa指針找到源類,并在源類的類方法列表中查找方法摇庙,所以要向源類中添加方法旱物。
Class metaClass = objc_getMetaClass([NSStringFromClass(objectClass) cStringUsingEncoding:NSUTF8StringEncoding]);
// 向源類中添加SEL為短方法名,IMP為帶MR_的方法實現(xiàn)
BOOL methodWasAdded = class_addMethod(metaClass,
originalSelector,
method_getImplementation(existingMethod),
method_getTypeEncoding(existingMethod));
// 返回添加結(jié)果
return methodWasAdded;
}
}
return NO;
}
MagicalRecord替換了動態(tài)方法解析方法resolveClassMethod :
和resolveInstanceMethod:
卫袒。由于使用短方法名找不到方法實現(xiàn)而進入動態(tài)解析過程宵呛,動態(tài)解析方法已被替換,進入自己寫的解析方法夕凝。根據(jù)方法名判斷是否是自己的方法宝穗,如果是户秤,在短方法名前添加MR_獲取方法實現(xiàn),通過class_addMethod
向類中添加一個方法逮矛。如果添加成功鸡号,返回YES,會重新尋找短方法名的方法實現(xiàn)须鼎,這時就能找到我們剛才添加的短方法名的方法鲸伴,從而實現(xiàn)了短方法名。