iOS-Code Data多线程的封装详解

来源:互联网 发布:如何查看淘宝交易快照 编辑:程序博客网 时间:2024/06/08 19:22
Code Data 的单例封装:

     很容易发现,系统生成的模版代码将Core Data 的基本的准备(貌似还挺复杂!)都放在 AppDelegate中了,可苹果公司为什么会这么做呢?
     我想,难道是考虑用户的体验?那苹果的用户体验还真是棒,连程序员的编程体验都考虑到!!!
     有时候,为了学习,我们并不需要别人把复杂的部分都帮我们干了!
     所以,自己封装一个单例是很有必要的!

     在使用FMDB中,我们知道,FMDB为了保证线程是安全的,它封装了一个异步执行嵌入了一个串行队列的同步执行的来保证线程是安全的!
     而Core Data好像并不是线程安全的;而我们使用频率比较低就是AppDelegate了吧,这样间接的保证了安全吧!我只是这样猜测!

      再来回顾一下Core Data的核心对象:
  • NSManagedObjectContext 管理对象上下文
  • NSManagedObjectModel 管理对象模型
  • NSPersistentStoreCoordinator 持久化存储调度器

下面,一步步封装一个Code Data单例!

1、新建项目command +shift + N     
      不勾选 User Core Data
2、新建CSCodeDataManger类

     实现单例,该单例,全局只留一个访问点,即重写allocWithZone


staticid instance ;+ (instancetype)sharedCodeDataManger {    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        instance = [[self alloc] init];    });    return instance;}+ (instancetype)allocWithZone:(struct _NSZone *)zone {    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        instance = [super allocWithZone:zone];    });    return instance;}

  小结:单例中allocWithZone的选择,
       我们都知道,实现allocWithZone,那么该单例对象在整个应用程序中则只有一个访问点,怎么选择? 
               如果单例对象提供的方法,允许用户进行私人定制,则无需实现 allocWithZone:
                    例如:NSURLSession,我们可以通过allow 方法实例化,并通过它的代理来监控实现的进度! 
               如果单例对象中提供的操作涉及到线程安全,并且确定没有定制需求,则应该实现allocWithZone: 方法
       
  在iOS-Code Data的快速体验:http://blog.csdn.net/yang198907/article/details/50521100中,我们知道对增/删/改/查
    我们需要的是一个NSManagedObjectContext的一个上下文,一个saveContext的一个方法,
  所以,我们需要在封装的单例中获取NSManagedObjectContext并提供一个saveContext的方法;
  所以,首先在单例.h中,向外声明一个NSManagedObjectContext的属性;和一个saveContext的方法;
   
     @property(readonly,strong,nonatomic)NSManagedObjectContext*managedObjectContext;
     - (BOOL)saveContext;

  所以两个步骤:
    (1) 实现managedObjectContext的getter方法;
    (2)实现saveContext;
     当然,这里有个注意点:
         只读属性,外界无法修改,内部会生成 _成员变量,但是一旦实现了getter 的方法。那么就需要使用@synthesize 指定成员变量;
          @synthesize managedObjectContext = _managedObjectContext;
     
     仔细观察系统为默认帮我们实现的方法;
     1、@property(readonly,strong,nonatomic)NSManagedObjectContext*managedObjectContext;
     2、@property(readonly,strong,nonatomic)NSManagedObjectModel*managedObjectModel;
     3、@property(readonly,strong,nonatomic)NSPersistentStoreCoordinator*persistentStoreCoordinator;
     - (NSURL*)applicationDocumentsDirectory;
     
     1、就是我们想要得到的管理对象上下文
     2、就是管理对象模型;
     3、就是 持久化存储调度器
          applicationDocumentsDirectory就是获取Core Data store file的路径;

          在来看这张图,会不会感觉清晰很多?




       目标更加清晰了:
         一、接下来实现managedObjectContext的getter方法
    1、首先创建模型:
          NSURL*modelURL = [[NSBundlemainBundle]URLForResource:@"CodeDataOC"withExtension:@"momd"];
        NSManagedObjectModel*managedObjectModel = [[NSManagedObjectModelalloc]initWithContentsOfURL:modelURL];
         
    2、根据数据模型创建数据库调度器
         NSPersistentStoreCoordinator*persistentStoreCoordinator = [[NSPersistentStoreCoordinatoralloc]initWithManagedObjectModel:managedObjectModel];
        //拼接数据库存放的路径
        NSURL*url = [[[NSFileManagerdefaultManager]URLsForDirectory:NSDocumentDirectoryinDomains:NSUserDomainMask]lastObject];
         NSURL*storeURL = [urlURLByAppendingPathComponent:saveCodeName];
           
                 //根据URL创建数据库
         NSError*error =nil;
         NSPersistentStore  *persistentStore = [persistentStoreCoordinatoraddPersistentStoreWithType:NSSQLiteStoreTypeconfiguration:nilURL:storeURLoptions:nilerror:&error];

     3、创建管理对象上下文 并指定 调度器
       _managedObjectContext= [[NSManagedObjectContextalloc]initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContextsetPersistentStoreCoordinator:persistentStoreCoordinator];

          

  二、实现saveContext
          这是系统的实现
     - (void)saveContext {
         NSManagedObjectContext*managedObjectContext = self.managedObjectContext;
         if(managedObjectContext != nil) {
             NSError*error = nil;
             if([managedObjectContexthasChanges] && ![managedObjectContextsave:&error]) {
                 NSLog(@"Unresolved error %@, %@", error, [erroruserInfo]);
                 abort();
             }
         }
     }
     改写为更加清晰明了的:    
     - (BOOL)saveContext {
         if(self.managedObjectContext==nil) {
             NSLog(@"上下文为nil,无法进行数据操作");
             returnNO;
         }
         if(![self.managedObjectContexthasChanges]) {
             NSLog(@"没有需要保存的数据");
             returnYES;
         }
         NSError*error = nil;
         if(![self.managedObjectContextsave:&error]) {
             NSLog(@"保存数据失败-> %@", error);
             returnNO;
         }
         returnYES;
     }

  至此,我们就完成了Code Data 的单例封装,
    接下来创建一个模型测试一下:
               新建一个模型:command + N 



 
     当然,如果至此就完毕的话,显然是不完善的,它只是支持单线程的,在实际的应用中,我们需要支持多线程的!
     接着往下搞!
     
     先画一个单线程的示意图:


也就是说,在单线程,增/删/改都是上下文来修改数据库的!
              
           而多线程的示意图:  
               后台上下文是主要的上下文,真正负责数据写入操作
               主线程上下文是后台上下文的子上下文,只是管理上下文的对象图,不与 PSC 直接交互!
               主线程中的save只是假操作,只是通知到后台进程,由后台进程来操作数据库;
     



             代码修改:
               1、增加一个后台backgoundManagedObjectContext属性
          @interfaceCSCodeDataManger()
          @property(readonly,strong,nonatomic)NSManagedObjectContext*backgoundManagedObjectContext;
          @end
               2、
                    (1)实例化_backgoundManagedObjectContext并设置其数据操作队列的类型,同时设置上下文数据库
                  (2)实例化_managedObjectContext并设置其父上下文
                 _backgoundManagedObjectContext = [[NSManagedObjectContextalloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType]
         [_backgoundManagedObjectContextsetPersistentStoreCoordinator:persistentStoreCoordinator];
   
        _managedObjectContext= [[NSManagedObjectContextalloc]initWithConcurrencyType:NSMainQueueConcurrencyType];
         [_managedObjectContextsetParentContext:_backgoundManagedObjectContext];
         
               3、修改saveContext函数
                    
                - (BOOL)saveContext {
             //判断上下文是否为nil
             if(self.managedObjectContext==nil||_backgroundMOC==nil) {
                 NSLog(@"上下文为nil,无法进行数据操作");
             returnNO;     
              }
              //判断是否有数据变化
             if(!self.managedObjectContext.hasChanges&& !_backgroundMOC.hasChanges) {
                 NSLog(@"没有需要保存的数据");
                 returnYES;
              }
              //进行保存数据
              NSError*error = nil;
              //主线程保存,只是把对象图变化通知后台上下文,不做磁盘写入操作
              if(![self.managedObjectContextsave:&error]) {
                  NSLog(@"保存数据失败-> %@", error);
                  returnNO;
              }
              //后台上下文在异步保存数据
              [_backgroundMOCsave:NULL];
              returnYES;
         }
         
         注意:可以注释掉  [_backgroundMOCsave:NULL]; 做一个测试,会发现,没有这句,数据并不会保存起来!

封装完毕后,以后的项目开发中将会非常方便,而且便于维护!

  说了这么多,赶紧测试一下吧!

代码我已经上传到我的github上了:

链接地址:https://github.com/yscGit/CSCodeDataManger



0 0
原创粉丝点击