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. Spark aggregateByKey函数

    aggregateByKey与aggregate类似,都是进行两次聚合,不同的是后者只对分区有效,前者对分区中key进一步细分 def aggregateByKey[U: ClassTag](zero ...

  2. Spring初学笔记(一):Spring IOC的理解

    关于依赖注入.控制反转 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的 ...

  3. 如何理解golang中的nil

    nil的奇怪行为 刚接触golang时,发现nil在不同的上下文,行为表现是不同的,并且和其他语言中的表现,也不大相同 实例1:输入true, true, false,不符合传递性 func main ...

  4. 马兴德201771010117《面向对象程序设计(java)》第一周学习总结

    第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.com 挽歌朽年 程序设计评测:https://pintia.cn/ 791683057@qq ...

  5. A+B Coming(hdu1720)

    思考:十六进制的输入->%x,定义时用int.要变成十进制输出,直接在输出时用->%d. #include<stdio.h> int main() { int A,B; cha ...

  6. ShoneSharp语言(S#)的设计和使用介绍系列(8)— 最炫“公式”风

    ShoneSharp语言(S#)的设计和使用介绍 系列(8)— 最炫“公式”风 作者:Shone 声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneSha ...

  7. 如何为Form表单的多个提交按钮指定不同的Action地址?

    这是我很久以前看到的一个技巧,但我忘记在哪里了,当时遇到这样的需求,做了笔记,现在整理成文章分享出来,因为我感觉这个小技巧还是挺有用的,这种应用场景也算比较常见,比如一个表单有"保存&quo ...

  8. [SD喜爱语言PK大赛]001.PHP vs Node.js

    引言:近日,两大编程飓风之战已经愈演愈烈.在程序员社区,一些争端因PHP与Node.js而起. 观点:其实就本人及团队而言,Language just a language!不存在高低之分,而侧重的原 ...

  9. 【转】sql用逗号连接多张表对应哪个join?

    逗号连接查询(用where连接条件): select order.id, order.orderdate,employee.id,employee.name from order,employee w ...

  10. 实现一个字符串匹配算法,从字符串 H 中,查找 是否存在字符串 Y ,若是存在返回所在位置的索引,不存在返回 -1(不基于indexOf/includes方法)

    /** 1.循环原始字符串的每一项,让每一项从当前位置向后截取 H.length 个字符, 然后和 Y 进行比较,如果不一样,继续循环:如果一样返回当前索引即可 **/ function myInde ...