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. 如何搭建一个WEB服务器项目(四)—— 实现安卓端图片加载

    使用Glide安卓图片加载库 观前提示:本系列文章有关服务器以及后端程序这些概念,我写的全是自己的理解,并不一定正确,希望不要误人子弟.欢迎各位大佬来评论区提出问题或者是指出错误,分享宝贵经验.先谢谢 ...

  2. 笨办法学习python-ex51自我理解笔记

    本章节主要讲的是web的工作原理,先大概熟悉记录一下,为以后写Django web框架打下基础. web工作原理: 1.用户从浏览器输入网址----->browser通过电脑中的网络设备(网卡) ...

  3. Mysql快速入门(看完这篇能够满足80%的日常开发)

    这是一篇mysql的学习笔记,整理结合了网上搜索的教程以及自己看的视频教程,看完这篇能够满足80%的日常开发了. 菜鸟教程:https://www.runoob.com/mysql/mysql-tut ...

  4. python3.x 基础三:装饰器

    装饰器:本质是函数,用于装饰其他函数,在不改变其他函数的调用和代码的前提下,增加新功能 原则: 1.不能修改被装饰函数的源代码 2.不能修改被装饰函数的调用方式 3.装饰函数对于被装饰函数透明 参考如 ...

  5. 王玉兰201771010128实验二 Java基本程序设计

    第一部分:理论知识学习部分:  (1)标识符:标识符由字母.下划线.美元符号和数字组成,且第一个符号不能为数字.Hello.$1234.程序名.www_123都是合法标识符.标识符可用作类名.变量名. ...

  6. Python - 常用的PyCharm的快捷键和使用场景介绍

    关于PyCharm的快捷键,由于数量众多,差不多有100个,相信几乎没有人会记住所有,每个人都会有自己顺手的几个,这里我将自己用着顺手,不别扭的快捷键分享出来,同时分享在哪里可以找到所有的快捷键. 一 ...

  7. pytest 使用

    import pytestfrom web_ui_YXBI.test_datas.common_datas import Common_Datas as cfrom selenium import w ...

  8. centos安装以及网络配置

    Linux安装 1.Linux安装完成后 第一个问题就是网络不通的问题 ,问题图片如下: 解决办法: 三种网络模式: 桥接模式:虚拟机和宿主机是兄弟关系,统一由宿主机连接的路由器分发ip NAT模式: ...

  9. JavaScript之倔强的字符串

    关于倔强的JavaScript字符串:不可以被修改 我们是字符串 我们的口号是:你可以消灭我,但是你不能改变我 JavaScript字符串是不可改变的,当真是这样的吗? 让我们来试验一下. var n ...

  10. C# 数据操作系列 - 19 FreeSql 入坑介绍

    0. 前言 前几天FreeSql的作者向我推荐了FreeSql框架,想让我帮忙写个文章介绍一下.嗯,想不到我也能带个货了.哈哈,开个玩笑-看了下觉得设计的挺有意思的,所以就谢了这篇文章. 简单介绍一下 ...