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: 方法
       
    我们需要的是一个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

iOS-Code Data多线程的封装详解的更多相关文章

  1. iOS开发--常用技巧 (MJRefresh详解)

         iOS开发--常用技巧 (MJRefresh详解) https://github.com/CoderMJLee/MJRefresh 下拉刷新01-默认 self.tableView.head ...

  2. 李洪强iOS经典面试题156 - Runtime详解(面试必备)

    李洪强iOS经典面试题156 - Runtime详解(面试必备)   一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C ...

  3. 【DataGuard】部署Data Guard相关参数详解 (转载)

    原文地址:[DataGuard]部署Data Guard相关参数详解 作者:secooler    有关物理Data Guard部署参考<[DataGuard]同一台主机实现物理Data Gua ...

  4. 18.Java 封装详解/多态详解/类对象转型详解

    封装概述 简述 封装是面向对象的三大特征之一. 封装优点 提高代码的安全性. 提高代码的复用性. "高内聚":封装细节,便于修改内部代码,提高可维护性. "低耦合&quo ...

  5. ASP.NET MVC Filters 4种默认过滤器的使用【附示例】 数据库常见死锁原因及处理 .NET源码中的链表 多线程下C#如何保证线程安全? .net实现支付宝在线支付 彻头彻尾理解单例模式与多线程 App.Config详解及读写操作 判断客户端是iOS还是Android,判断是不是在微信浏览器打开

    ASP.NET MVC Filters 4种默认过滤器的使用[附示例]   过滤器(Filters)的出现使得我们可以在ASP.NET MVC程序里更好的控制浏览器请求过来的URL,不是每个请求都会响 ...

  6. ios开发——实战OC篇&FMDB详解

    FMDB详解 前一篇文章中我们介绍的SQLite的使用,在iOS中原生的SQLite API在使用上相当不友好. 于是,就出现了一系列将SQLite API进行封装的库,例如FMDB.Plausibl ...

  7. iOS开发——屏幕适配篇&Masonry详解

    Masonry详解 前言 MagicNumber -> autoresizingMask -> autolayout 以上是纯手写代码所经历的关于页面布局的三个时期 在iphone1-ip ...

  8. iOS学习——iOS项目Project 和 Targets配置详解

    最近开始学习完整iOS项目的开发流程和思路,在实际的项目开发过程中,我们通常需要对项目代码和资料进行版本控制和管理,一般比较常用的SVN或者Github进行代码版本控制和项目管理.我们iOS项目的开发 ...

  9. iOS开发——UI篇OC&transform详解

    transframe属性详解 1. transform属性 在OC中,通过transform属性可以修改对象的平移.缩放比例和旋转角度 常用的创建transform结构体方法分两大类 (1) 创建“基 ...

随机推荐

  1. PAT 1007 Maximum Subsequence Sum (25分)

    题目 Given a sequence of K integers { N​1​​ , N​2​​ , ..., N​K​​ }. A continuous subsequence is define ...

  2. 终于,帮开发写了一个bug

    写在文章的开头 最近项目比较紧,尤其前端的的需求比较多,作为一名测试,也会些vue,本着加快项目进度的美好想法,就自告奋勇的向组长承接了一部分开发的任务,其中有个需求需要在我们的广告管理后台新增一个上 ...

  3. 微信小程序-视频弹幕的项目

    1.视频播放器 2.选择弹幕颜色 3.弹幕来了... 一般微信小程序需要配置.wxml.wxss.js.json文件,所有接下来也是要配置这几个文件,请看下图: 第一:  index.wxml < ...

  4. Rabbitmq 报错 nodedown

    问题描述 线上 rabbitmq 集群账号密码设置的过于简单,有一定的风险.在整改过程中发现,三台机器信息错乱,每台服务器执行rabbitmq 相关的命令就报错,Error: unable to co ...

  5. binlog在并发状态下的记录

    前两天看binlog发现个奇怪的地方:对于position靠后的记录,timestamp却比之前的记录还要小.当时觉得大概和并发有关系 后来做了个实验 开两个session 对于session1: b ...

  6. Spring 中基于 AOP 的 XML架构

    Spring 中基于 AOP 的 XML架构 为了使用 aop 命名空间标签,你需要导入 spring-aop j架构,如下所述: <?xml version="1.0" e ...

  7. 一个文件搞定Asp.net core 3.1动态页面转静态页面

    最近一个Asp.net core项目需要静态化页面,百度查找了一下,没有发现合适的.原因如下 配置麻烦. 类库引用了第三方类,修改起来麻烦. 有只支持MVC,不支持PageModel. 继承Actio ...

  8. springboot连接数据源,配置数据库

    Spring官方DriverManagerDataSource的配置 修改yml #配置数据源的属性 spring: datasource: driver-class-name: com.mysql. ...

  9. TCP实现连接传输案例

    使用的类    ServerSocket 服务器端        构造方法:ServerSocket(端口号); 接收端使用    方法:accept(); 返回一个 Socket对象    getI ...

  10. 跳出初学MySQL知识的原理整理(一)

    一.基础架构 MySQL 可以分为 Server 层和存储引擎层两部分. Server 层包括连接器.查询缓存.分析器.优化器.执行器等,所有跨存储引擎 的功能都在这一层实现,比如存储过程.触发器.视 ...