MagicalRecord源碼分析

MagicalRecord是一個(gè)簡(jiǎn)化CoreData操作的工具庫,既然用到了這個(gè)庫,那就看一下源碼了解一下這個(gè)庫是怎么工作的,我閱讀的版本是2.3.2。

MagicalRecord的好處

  • 清理我的Core Data相關(guān)代碼
  • 支持清晰,簡(jiǎn)單,一行代碼式的查詢
  • 當(dāng)需要優(yōu)化請(qǐng)求時(shí),仍然可以修改 NSFetchRequest

MagicalRecord源碼

MagicalRecord的初始化

我們使用CoreData通常是這樣的:

  1. 根據(jù)momd路徑創(chuàng)建托管對(duì)象模型
  2. 根據(jù)托管對(duì)象模型創(chuàng)建持久化存儲(chǔ)協(xié)調(diào)器
  3. 創(chuàng)建并關(guān)聯(lián)SQLite數(shù)據(jù)庫文件
  4. 創(chuàng)建管理對(duì)象上下文并指定持久化存儲(chǔ)協(xié)調(diào)器
// 創(chuàng)建托管對(duì)象模型,并使用CoreData.momd路徑當(dāng)做初始化參數(shù)
NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"CoreData" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath];

// 根據(jù)托管對(duì)象模型創(chuàng)建持久化存儲(chǔ)協(xié)調(diào)器
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

// 創(chuàng)建并關(guān)聯(lián)SQLite數(shù)據(jù)庫文件,如果已經(jīng)存在則不會(huì)重復(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)建上下文對(duì)象,并發(fā)隊(duì)列設(shè)置為主隊(duì)列
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

// 指定持久化存儲(chǔ)協(xié)調(diào)器
context.persistentStoreCoordinator = coordinator;

而使用MagicalRecord我們只需要調(diào)用[MagicalRecord setupCoreDataStack]
這個(gè)方法的具體實(shí)現(xiàn)是

+ (void) setupCoreDataStack
{
    [self setupCoreDataStackWithStoreNamed:[self defaultStoreName]];
}

+ (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName
{
    //判斷默認(rèn)持久化存儲(chǔ)協(xié)調(diào)器是否存在
    if ([NSPersistentStoreCoordinator MR_defaultStoreCoordinator] != nil) return;
    //不存在就創(chuàng)建持久化存儲(chǔ)協(xié)調(diào)器
    NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithSqliteStoreNamed:storeName];
    //設(shè)置默認(rèn)持久化存儲(chǔ)協(xié)調(diào)器
    [NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:coordinator];
    //創(chuàng)建NSManagedObjectContext
    [NSManagedObjectContext MR_initializeDefaultContextWithCoordinator:coordinator];
}

創(chuàng)建持久化存儲(chǔ)協(xié)調(diào)器

+ (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName withOptions:(NSDictionary *)options
{
    //獲取默認(rèn)托管對(duì)象模型并創(chuàng)建持久化存儲(chǔ)協(xié)調(diào)器
    NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel];
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    //添加持久化存儲(chǔ)到協(xié)調(diào)器
    [psc MR_addSqliteStoreNamed:storeFileName withOptions:options];
    return psc;
}

添加SQLite存儲(chǔ)

- (NSPersistentStore *) MR_addSqliteStoreNamed:(id)storeFileName configuration:(NSString *)configuration withOptions:(__autoreleasing NSDictionary *)options
{
    //持久化存儲(chǔ)URL
    NSURL *url = [storeFileName isKindOfClass:[NSURL class]] ? storeFileName : [NSPersistentStore MR_urlForStoreName:storeFileName];
    NSError *error = nil;
    //創(chuàng)建持久化存儲(chǔ)文件
    [self MR_createPathToStoreFileIfNeccessary:url];
    
    NSPersistentStore *store = [self addPersistentStoreWithType:NSSQLiteStoreType
                                                  configuration:configuration
                                                            URL:url
                                                        options:options
                                                          error:&error];
    
    //數(shù)據(jù)庫不存在
    if (!store)
    {
        /*
          如果工程有DEBUG標(biāo)記,則kMagicalRecordShouldDeleteStoreOnModelMismatch被設(shè)為YES
          此時(shí)使用默認(rèn)的SQLite數(shù)據(jù)存儲(chǔ),不創(chuàng)建新的版本的數(shù)據(jù)模型而是直接改變數(shù)據(jù)模型本身的方式,將會(huì)刪除舊的存儲(chǔ)并自動(dòng)創(chuàng)建一個(gè)新的。
          這會(huì)節(jié)省大量的時(shí)間 - 不再需要在改變數(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)建持久化存儲(chǔ)
                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;
}

在平時(shí)開發(fā)過程中,經(jīng)常會(huì)出現(xiàn)修改模型結(jié)構(gòu)的情況,需要把a(bǔ)pp卸載了然后重新安裝才能避免打不開數(shù)據(jù)庫導(dǎo)致崩潰的問題。MagicalRecord的處理方式是在DEBUG模式下將數(shù)據(jù)庫直接刪除重新創(chuàng)建,可以節(jié)省很多時(shí)間。

接著創(chuàng)建管理對(duì)象上下文

+ (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種

  • 兩個(gè)上下文,一個(gè)協(xié)調(diào)器
    一個(gè)位于主線程的上下文,一個(gè)位于后臺(tái)線程的上下文,共用一個(gè)協(xié)調(diào)器。UI相關(guān)的操作在主線程的上下文上進(jìn)行,后臺(tái)操作放入后臺(tái)線程執(zhí)行。當(dāng)后臺(tái)線程上下文保存后,通過監(jiān)聽NSManagedObjectContextDidSaveNotification來更新主線程的上下文中的數(shù)據(jù)。把獲取和保存等操作放入后臺(tái)線程來避免阻塞主線程。由于使用同一個(gè)協(xié)調(diào)器,兩個(gè)上下文可以共用同一個(gè)行緩存,避免了一些對(duì)數(shù)據(jù)庫不必要的訪問,提高了性能。但是由于共享一個(gè)協(xié)調(diào)器,所以在同一時(shí)間只有一個(gè)上下文能使用協(xié)調(diào)器。并且要設(shè)置合適的合并策略來解決多個(gè)上下文都有更改是造成的沖突。

  • 兩個(gè)協(xié)調(diào)器
    兩個(gè)獨(dú)立的協(xié)調(diào)器,共享同一個(gè)SQLite數(shù)據(jù)庫。創(chuàng)建一個(gè)主線程上下文,連接一個(gè)協(xié)調(diào)器,一個(gè)后臺(tái)線程上下文,連接一個(gè)協(xié)調(diào)器。這樣的設(shè)置可以減少對(duì)協(xié)調(diào)器的競(jìng)爭(zhēng)問題,提供了更好的并發(fā)處理能力,因?yàn)镾QLite支持多讀單寫,在不進(jìn)行多個(gè)寫操作時(shí),不會(huì)產(chǎn)生競(jìng)爭(zhēng)問題。但是由于行緩存位于協(xié)調(diào)器層,所以行緩存不能共享。也就是說如果某一個(gè)上下文修改了數(shù)據(jù),另一個(gè)上下文需要獲取到這個(gè)數(shù)據(jù)需要下降到數(shù)據(jù)庫層。訪問數(shù)據(jù)庫相比于訪問內(nèi)存效率要低。

  • 嵌套上下文
    一個(gè)位于主線程的上下文,一個(gè)位于后臺(tái)線程的上下文,將后臺(tái)線程上下文直接與協(xié)調(diào)器相連,主線程上下文的父上下文設(shè)置為后臺(tái)線程上下文。使用這種設(shè)置,主線程上下文的操作完全在內(nèi)存中進(jìn)行,因?yàn)樗械牟僮髦粫?huì)被push到父上下文中,當(dāng)父上下文進(jìn)行保存操作時(shí),才會(huì)通過持久化存儲(chǔ)協(xié)調(diào)器訪問數(shù)據(jù)庫,由于父上下文是一個(gè)私有隊(duì)列上下文,這些操作不會(huì)阻塞UI線程。

MagicalRecord使用的是嵌套上下文的設(shè)計(jì)。原因可能是相比于其他兩種設(shè)計(jì),嵌套上下文使用簡(jiǎn)單,管理方便,并且也能很好的分離UI操作和后臺(tái)數(shù)據(jù)操作,雖然性能不是最高的,但是適用于大部分APP。

MagicalRecord保存

MagicalRecord提供了defaultContext讓我們?cè)谥骶€程處理有關(guān)UI的數(shù)據(jù)。如果我們想在后臺(tái)操作數(shù)據(jù),可以使用+ saveWithBlock:completion:函數(shù)。這個(gè)函數(shù)用于更改實(shí)體的Block永遠(yuǎn)不會(huì)在主線程執(zhí)行,并且提供一個(gè)rootContext的子context。當(dāng)Block中的操作執(zhí)行完畢,會(huì)回調(diào)completion block,這個(gè)block在主線程中調(diào)用,所以可以在此block里安全觸發(fā)UI更新。

+ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion;
{
    //創(chuàng)建一個(gè)父上下文是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];
    }];
}

而要進(jìn)行同步保存的話使用MR_saveToPersistentStoreAndWait函數(shù)

- (void) MR_saveToPersistentStoreAndWait
{
    [self MR_saveWithOptions:MRSaveParentContexts | MRSaveSynchronously completion:nil];
}

在保存選項(xiàng)中增加了同步保存的選項(xiàng),如何進(jìn)行同步和后臺(tái)的存儲(chǔ),讓我們看看保存數(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])
            {
                // 添加或移除同步選項(xiàng)
                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ù)時(shí)根據(jù)saveOptions決定是否同步以及父上下文是否保存。如果同步,調(diào)用performBlockAndWait:函數(shù),否則調(diào)用performBlock:函數(shù)。通過遞歸調(diào)用的方式進(jìn)行父上下文的保存直到rootContext將數(shù)據(jù)通過 Persistent Store Coordinator存入數(shù)據(jù)庫。在其它線程保存完畢后,默認(rèn)上下文如何更新數(shù)據(jù)呢?在設(shè)置默認(rèn)上下文時(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存儲(chǔ)完畢的通知
    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)后臺(tái)線程操作完數(shù)據(jù),默認(rèn)上下文接到通知合并更改。

MagicalRecord增刪改查

創(chuàng)建實(shí)體對(duì)象

+ (id) MR_createEntityInContext:(NSManagedObjectContext *)context
{
    if ([self respondsToSelector:@selector(insertInManagedObjectContext:)] && context != nil)
    {
        //判斷是否實(shí)現(xiàn)了`insertInManagedObjectContext:`函數(shù),如果可以通過這個(gè)函數(shù)創(chuàng)建實(shí)體
        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];
    }
}

刪除實(shí)體

- (BOOL) MR_deleteEntityInContext:(NSManagedObjectContext *)context
{
    NSError *error = nil;
    //跨上下文不能直接訪問實(shí)體對(duì)象,通過實(shí)體對(duì)象的ID判斷當(dāng)前上下文中是否存在這個(gè)實(shí)體對(duì)象
    NSManagedObject *entityInContext = [context existingObjectWithID:[self objectID] error:&error];
    [MagicalRecord handleErrors:error];
    //如果實(shí)體對(duì)象存在就刪除
    if (entityInContext) {
        [context deleteObject:entityInContext];
    }

    return YES;
}

刪除全部實(shí)體

+ (BOOL) MR_truncateAllInContext:(NSManagedObjectContext *)context
{
    //創(chuàng)建獲取所有實(shí)體的請(qǐng)求
    NSFetchRequest *request = [self MR_requestAllInContext:context];
    //設(shè)置返回值為惰值
    [request setReturnsObjectsAsFaults:YES];
    //只獲取ObjectID
    [request setIncludesPropertyValues:NO];

    //查找要?jiǎng)h除的數(shù)據(jù)
    NSArray *objectsToDelete = [self MR_executeFetchRequest:request inContext:context];
    for (NSManagedObject *objectToDelete in objectsToDelete)
    {
        //刪除數(shù)據(jù)
        [objectToDelete MR_deleteEntityInContext:context];
    }
    return YES;
}

刪除全部實(shí)體時(shí),通過使用惰值和ObjectID減少對(duì)內(nèi)存的占用。也可以使用MR_deleteAllMatchingPredicate:inContext:通過指定謂詞來刪除符合條件的數(shù)據(jù)。

MagicalRecord的查找

+ MR_findAllInContext:context
+ MR_findAllSortedBy:ascending:inContext:
+ MR_findAllSortedBy:ascending:withPredicate:inContext:
+ MR_findAllWithPredicate:inContext:

這些查找方法都會(huì)調(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的方法交換和動(dòng)態(tài)方法解析來實(shí)現(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);

    // 獲取目標(biāo)類的源類
    Class targetMetaClass = objc_getMetaClass([NSStringFromClass(targetClass) cStringUsingEncoding:NSUTF8StringEncoding]);

    //向目標(biāo)源類中添加一個(gè)方法,SEL為MR_resolveClassMethod:方法的SEL,IMP為resolveInstanceMethod:的IMP
    BOOL methodWasAdded = class_addMethod(targetMetaClass, sourceSelector,
                                          method_getImplementation(targetClassMethod),
                                          method_getTypeEncoding(targetClassMethod));

    if (methodWasAdded)
    {
        //把目標(biāo)源類中的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:實(shí)現(xiàn)無法解析SEL,向本類添加短方法名的實(shí)現(xiàn)
        resolvedClassMethod = MRAddShortHandMethodForPrefixedClassMethod(self, originalSelector, kMagicalRecordCategoryPrefix);
    }
    // 返回YES重新發(fā)送消息,返回NO則進(jìn)入消息轉(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ù)名稱判斷是否添加實(shí)現(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_的方法實(shí)現(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ù)名稱判斷是否添加實(shí)現(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)
        {
            // 獲取本類的源類。
            // 因?yàn)轭悓?duì)象的方法列表在源類中,調(diào)用類方法時(shí),通過類對(duì)象指向源類的isa指針找到源類,并在源類的類方法列表中查找方法,所以要向源類中添加方法。
            Class metaClass = objc_getMetaClass([NSStringFromClass(objectClass) cStringUsingEncoding:NSUTF8StringEncoding]);

            // 向源類中添加SEL為短方法名,IMP為帶MR_的方法實(shí)現(xiàn)
            BOOL methodWasAdded = class_addMethod(metaClass,
                                                  originalSelector,
                                                  method_getImplementation(existingMethod),
                                                  method_getTypeEncoding(existingMethod));

            // 返回添加結(jié)果
            return methodWasAdded;
        }
    }
    return NO;
}

MagicalRecord替換了動(dòng)態(tài)方法解析方法resolveClassMethod :resolveInstanceMethod:。由于使用短方法名找不到方法實(shí)現(xiàn)而進(jìn)入動(dòng)態(tài)解析過程,動(dòng)態(tài)解析方法已被替換,進(jìn)入自己寫的解析方法。根據(jù)方法名判斷是否是自己的方法,如果是,在短方法名前添加MR_獲取方法實(shí)現(xiàn),通過class_addMethod向類中添加一個(gè)方法。如果添加成功,返回YES,會(huì)重新尋找短方法名的方法實(shí)現(xiàn),這時(shí)就能找到我們剛才添加的短方法名的方法,從而實(shí)現(xiàn)了短方法名。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容