MagicalRecord源码分析

2018-01-04  本文已影响0人  浅笑11

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

MagicalRecord的好处

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种

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,会重新寻找短方法名的方法实现,这时就能找到我们刚才添加的短方法名的方法,从而实现了短方法名。

上一篇下一篇

猜你喜欢

热点阅读