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. 5、打断点(bpu)

    前言 先给大家讲一则小故事,在我们很小的时候是没有手机的,那时候跟女神聊天都靠小纸条.某屌丝A男对隔壁小王的隔壁女神C倾慕已久,于是天天小纸条骚扰,无奈中间隔着一个小王,这样小王就负责传小纸条了.有一 ...

  2. mp4封装格式各box类型讲解及IBP帧计算

    mp4封装格式各box类型讲解及IBP帧计算 目录 mp4封装格式各box类型讲解及IBP帧计算 box ftyp box moov box mvhd box (Movie Header Box) t ...

  3. Linux系统如何安装qt-desinger

    前言:最近想在python3.7.3下玩下PyQt5,写UI有两种方式一种是使用手写,第二个就是使用Qt Designer工具来写,所以首先就是安装PyQt5和pyqt5-tools工具了. 一.安装 ...

  4. Kubernetes实战 - 从零开始搭建微服务 1 - 使用kind构建一个单层架构Node/Express网络应用程序

    使用kind构建一个单层架构Node/Express网络应用程序 Kubernetes实战-从零开始搭建微服务 1 前言 准备写一个Kubernetes实战系列教程,毕竟cnblogs作为国内最早的技 ...

  5. 14.3 Go iris

    14.3 Go iris 下载 go get -u -v github.com/kataras/iris 代码示例 package main import "github.com/katar ...

  6. Vue刷新页面的三种方式

    我们在写项目的时候,经常会遇到,用户执行完某个动作,改变了某些状态,需要重新刷新页面,以此来重新渲染页面 1.原始方法: location.reload(); 2.vue自带的路由跳转: this.$ ...

  7. 就为了一个原子操作,其他CPU核心罢工了

    i++问题 "阿Q赶快回去吧,隔壁二号车间的虎子说我们改了他们的数据,上门来闹事了" 由于老K的突然出现,我不得不提前结束与小黑的交流,赶回了CPU一号车间. 见到我回来,虎子立刻 ...

  8. BZOJ 1084DP

    1084: [SCOI2005]最大子矩阵 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2796  Solved: 1391[Submit][Sta ...

  9. Robot Framework(3)- 基本概念

    如果你还想从头学起Robot Framework,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1770899.html 前言 在 RF ...

  10. MySQL的CHAR 和 VARCHAR的区别

    CHAR 和 VARCHAR 类型,CHAR 列的长度固定, VARCHAR 列中的值为可变长字符串.在检索的时候,CHAR 列删除了尾部的空格,而 VARCHAR 则保留这些空格s