MagicalRecord源码分析

MagicalRecord是一个简化CoreData操作的工具库,既然用到了这个库,那就看一下源码了解一下这个库是怎么工作的,我阅读的版本是2.3.2。

MagicalRecord的好处

  • 清理我的Core Data相关代码
  • 支持清晰,简单,一行代码式的查询
  • 当需要优化请求时,仍然可以修改 NSFetchRequest

MagicalRecord源码

MagicalRecord的初始化

我们使用CoreData通常是这样的:

  1. 根据momd路径创建托管对象模型
  2. 根据托管对象模型创建持久化存储协调器
  3. 创建并关联SQLite数据库文件
  4. 创建管理对象上下文并指定持久化存储协调器
// 创建托管对象模型,并使用CoreData.momd路径当做初始化参数
NSURL *modelPath = [[NSBundle mainBundle] URLForResource:@"CoreData" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelPath];

// 根据托管对象模型创建持久化存储协调器
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

// 创建并关联SQLite数据库文件,如果已经存在则不会重复创建
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];

// 创建上下文对象,并发队列设置为主队列
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

// 指定持久化存储协调器
context.persistentStoreCoordinator = coordinator;

而使用MagicalRecord我们只需要调用[MagicalRecord setupCoreDataStack]
这个方法的具体实现是

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

+ (void) setupCoreDataStackWithStoreNamed:(NSString *)storeName
{
    //判断默认持久化存储协调器是否存在
    if ([NSPersistentStoreCoordinator MR_defaultStoreCoordinator] != nil) return;
    //不存在就创建持久化存储协调器
    NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithSqliteStoreNamed:storeName];
    //设置默认持久化存储协调器
    [NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:coordinator];
    //创建NSManagedObjectContext
    [NSManagedObjectContext MR_initializeDefaultContextWithCoordinator:coordinator];
}

创建持久化存储协调器

+ (NSPersistentStoreCoordinator *) MR_coordinatorWithSqliteStoreNamed:(NSString *)storeFileName withOptions:(NSDictionary *)options
{
    //获取默认托管对象模型并创建持久化存储协调器
    NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel];
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    //添加持久化存储到协调器
    [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;
    //创建持久化存储文件
    [self MR_createPathToStoreFileIfNeccessary:url];
    
    NSPersistentStore *store = [self addPersistentStoreWithType:NSSQLiteStoreType
                                                  configuration:configuration
                                                            URL:url
                                                        options:options
                                                          error:&error];
    
    //数据库不存在
    if (!store)
    {
        /*
          如果工程有DEBUG标记,则kMagicalRecordShouldDeleteStoreOnModelMismatch被设为YES
          此时使用默认的SQLite数据存储,不创建新的版本的数据模型而是直接改变数据模型本身的方式,将会删除旧的存储并自动创建一个新的。
          这会节省大量的时间 - 不再需要在改变数据模型后每次都重新卸载和安装应用
        */
        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;
                // 无法打开数据库就删除
                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];
                // 再次创建持久化存储
                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;
}

在平时开发过程中,经常会出现修改模型结构的情况,需要把app卸载了然后重新安装才能避免打不开数据库导致崩溃的问题。MagicalRecord的处理方式是在DEBUG模式下将数据库直接删除重新创建,可以节省很多时间。

接着创建管理对象上下文

+ (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有很多种设置方式,常用有3种

  • 两个上下文,一个协调器
    一个位于主线程的上下文,一个位于后台线程的上下文,共用一个协调器。UI相关的操作在主线程的上下文上进行,后台操作放入后台线程执行。当后台线程上下文保存后,通过监听NSManagedObjectContextDidSaveNotification来更新主线程的上下文中的数据。把获取和保存等操作放入后台线程来避免阻塞主线程。由于使用同一个协调器,两个上下文可以共用同一个行缓存,避免了一些对数据库不必要的访问,提高了性能。但是由于共享一个协调器,所以在同一时间只有一个上下文能使用协调器。并且要设置合适的合并策略来解决多个上下文都有更改是造成的冲突。

  • 两个协调器
    两个独立的协调器,共享同一个SQLite数据库。创建一个主线程上下文,连接一个协调器,一个后台线程上下文,连接一个协调器。这样的设置可以减少对协调器的竞争问题,提供了更好的并发处理能力,因为SQLite支持多读单写,在不进行多个写操作时,不会产生竞争问题。但是由于行缓存位于协调器层,所以行缓存不能共享。也就是说如果某一个上下文修改了数据,另一个上下文需要获取到这个数据需要下降到数据库层。访问数据库相比于访问内存效率要低。

  • 嵌套上下文
    一个位于主线程的上下文,一个位于后台线程的上下文,将后台线程上下文直接与协调器相连,主线程上下文的父上下文设置为后台线程上下文。使用这种设置,主线程上下文的操作完全在内存中进行,因为所有的操作只会被push到父上下文中,当父上下文进行保存操作时,才会通过持久化存储协调器访问数据库,由于父上下文是一个私有队列上下文,这些操作不会阻塞UI线程。

MagicalRecord使用的是嵌套上下文的设计。原因可能是相比于其他两种设计,嵌套上下文使用简单,管理方便,并且也能很好的分离UI操作和后台数据操作,虽然性能不是最高的,但是适用于大部分APP。

MagicalRecord保存

MagicalRecord提供了defaultContext让我们在主线程处理有关UI的数据。如果我们想在后台操作数据,可以使用+ saveWithBlock:completion:函数。这个函数用于更改实体的Block永远不会在主线程执行,并且提供一个rootContext的子context。当Block中的操作执行完毕,会回调completion block,这个block在主线程中调用,所以可以在此block里安全触发UI更新。

+ (void) saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion;
{
    //创建一个父上下文是rootContext的上下文,类型是NSPrivateQueueConcurrencyType
    NSManagedObjectContext *savingContext  = [NSManagedObjectContext MR_rootSavingContext];
    NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextWithParent:savingContext];

    //子上下文通过performBlock回调到自己的线程中执行block
    [localContext performBlock:^{
        [localContext MR_setWorkingName:NSStringFromSelector(_cmd)];

        if (block) {
            block(localContext);
        }
        //保存数据
        [localContext MR_saveWithOptions:MRSaveParentContexts completion:completion];
    }];
}

而要进行同步保存的话使用MR_saveToPersistentStoreAndWait函数

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

在保存选项中增加了同步保存的选项,如何进行同步和后台的存储,让我们看看保存数据的具体代码

- (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;
    }
    //父上下文是否应该保存
    BOOL shouldSaveParentContexts = ((saveOptions & MRSaveParentContexts) == MRSaveParentContexts);
    //是否应该同步保存
    BOOL shouldSaveSynchronously = ((saveOptions & MRSaveSynchronously) == MRSaveSynchronously);
    //是否应该同步保存除了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)
                {
                    //成功回调主线程
                    dispatch_async(dispatch_get_main_queue(), ^{
                        completion(saveResult, error);
                    });
                }
            }
        }
    };

    if (saveSynchronously)
    {
        //通过performBlockAndWait:调度到自己的线程中保存并阻塞线程直到block处理结束
        [self performBlockAndWait:saveBlock];
    }
    else
    {
        //调度到自己的线程中保存,不阻塞线程
        [self performBlock:saveBlock];
    }
}

保存数据时根据saveOptions决定是否同步以及父上下文是否保存。如果同步,调用performBlockAndWait:函数,否则调用performBlock:函数。通过递归调用的方式进行父上下文的保存直到rootContext将数据通过 Persistent Store Coordinator存入数据库。在其它线程保存完毕后,默认上下文如何更新数据呢?在设置默认上下文时已经监听了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)
    {
        //不是主线程重新在主线程中调用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];
}

当后台线程操作完数据,默认上下文接到通知合并更改。

MagicalRecord增删改查

创建实体对象

+ (id) MR_createEntityInContext:(NSManagedObjectContext *)context
{
    if ([self respondsToSelector:@selector(insertInManagedObjectContext:)] && context != nil)
    {
        //判断是否实现了`insertInManagedObjectContext:`函数,如果可以通过这个函数创建实体
        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;
        }
        //创建entity并返回
        return [[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
    }
}

删除实体

- (BOOL) MR_deleteEntityInContext:(NSManagedObjectContext *)context
{
    NSError *error = nil;
    //跨上下文不能直接访问实体对象,通过实体对象的ID判断当前上下文中是否存在这个实体对象
    NSManagedObject *entityInContext = [context existingObjectWithID:[self objectID] error:&error];
    [MagicalRecord handleErrors:error];
    //如果实体对象存在就删除
    if (entityInContext) {
        [context deleteObject:entityInContext];
    }

    return YES;
}

删除全部实体

+ (BOOL) MR_truncateAllInContext:(NSManagedObjectContext *)context
{
    //创建获取所有实体的请求
    NSFetchRequest *request = [self MR_requestAllInContext:context];
    //设置返回值为惰值
    [request setReturnsObjectsAsFaults:YES];
    //只获取ObjectID
    [request setIncludesPropertyValues:NO];

    //查找要删除的数据
    NSArray *objectsToDelete = [self MR_executeFetchRequest:request inContext:context];
    for (NSManagedObject *objectToDelete in objectsToDelete)
    {
        //删除数据
        [objectToDelete MR_deleteEntityInContext:context];
    }
    return YES;
}

删除全部实体时,通过使用惰值和ObjectID减少对内存的占用。也可以使用MR_deleteAllMatchingPredicate:inContext:通过指定谓词来删除符合条件的数据。

MagicalRecord的查找

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

这些查找方法都会调用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:函数返回查询结果。

MagicalRecord还提供了短方法名,比如用 findAll 代替 MR_findAll,调用[MagicalRecord enableShorthandMethods]开启。通过Runtime的方法交换和动态方法解析来实现。

+ (void)enableShorthandMethods
{
    if (kMagicalRecordShorthandMethodsSwizzled == NO)
    {
        NSArray *classes = [self classesToSwizzle];
        //将数组中的类的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:实现无法解析SEL,向本类添加短方法名的实现
        resolvedClassMethod = MRAddShortHandMethodForPrefixedClassMethod(self, originalSelector, kMagicalRecordCategoryPrefix);
    }
    // 返回YES重新发送消息,返回NO则进入消息转发
    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);

    // 根据名称判断是否添加实现
    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_的方法实现
            BOOL methodWasAdded = class_addMethod(objectClass,
                                                  originalSelector,
                                                  method_getImplementation(existingMethod),
                                                  method_getTypeEncoding(existingMethod));

            // 返回添加结果
            return methodWasAdded;
        }
    }
    return NO;
}

static BOOL MRAddShortHandMethodForPrefixedClassMethod(Class objectClass, SEL originalSelector, NSString *prefix)
{
    NSString *originalSelectorString = NSStringFromSelector(originalSelector);

    // 根据名称判断是否添加实现
    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)
        {
            // 获取本类的源类。
            // 因为类对象的方法列表在源类中,调用类方法时,通过类对象指向源类的isa指针找到源类,并在源类的类方法列表中查找方法,所以要向源类中添加方法。
            Class metaClass = objc_getMetaClass([NSStringFromClass(objectClass) cStringUsingEncoding:NSUTF8StringEncoding]);

            // 向源类中添加SEL为短方法名,IMP为带MR_的方法实现
            BOOL methodWasAdded = class_addMethod(metaClass,
                                                  originalSelector,
                                                  method_getImplementation(existingMethod),
                                                  method_getTypeEncoding(existingMethod));

            // 返回添加结果
            return methodWasAdded;
        }
    }
    return NO;
}

MagicalRecord替换了动态方法解析方法resolveClassMethod :resolveInstanceMethod:。由于使用短方法名找不到方法实现而进入动态解析过程,动态解析方法已被替换,进入自己写的解析方法。根据方法名判断是否是自己的方法,如果是,在短方法名前添加MR_获取方法实现,通过class_addMethod向类中添加一个方法。如果添加成功,返回YES,会重新寻找短方法名的方法实现,这时就能找到我们刚才添加的短方法名的方法,从而实现了短方法名。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容