Core Data 网络应用实例

2024-06-23 23:48
文章标签 core data 网络应用 实例

本文主要是介绍Core Data 网络应用实例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自:http://www.cocoachina.com/applenews/devnews/2014/0430/8275.html


转自 answer_huang的博客

几乎每一个应用开发者都需要经历的就是将从 web service 获取到的数据转变到 Core Data 中。这篇文章阐述了如何去做。我们在这里讨论的每一个问题在之前的文章中都已经描述过了,并且 Apple 在他们的文档中也提过。然而,从头到尾回顾一遍对我们来说还是很有益的。

 

程序所有的代码都 在 GitHub 上
计划
我们将会建立一个简单、只读的应用程序,用来显示 CocoaPods 说明的完整列表。这些说明都显示在 table view 中,所有 pod 的说明都是以分页的形式,从 web service 取得,并以 JSON 对象返回。
我们这样来做
1.首先,我们创建一个 PodsWebservice 类,用来从 web service 请求所有的说明。
2.接着,创建一个 Importer 对象取出说明并将他们导入 Core Data。
3.最终,我们展示如何让最重要的工作在后台线程中运行。
从 Web Service 取得对象
首先,创建一个单独的类从 web service 取得数据是很不错的。我们已经写了一个简单的 web server 示例,用来获取 CocoaPods 说明并将它们生成 JSON;请求 /specs 这个 URL 会返回一个按字母排序的 pod 说明列表。web service 是分页的,所以我们需要分开请求每一页。一个响应的示例如下:
  
  1. {  
  2.   "number_of_pages": 559, 
  3.   "result": [{ 
  4.     "authors": { "Ash Furrow""ash@ashfurrow.com" }, 
  5.     "homepage""https://github.com/500px/500px-iOS-api"
  6.     "license""MIT"
  7.     "name""500px-iOS-api"
  8.   ... 
我们想要创建只有一个 fetchAllPods: 方法的类,它有一个回调 block,这将会被每一个页面调用。这也可以通过代理实现;但为什么我们选择用 block,你可以读一读这篇有关消息传递机制的文章。
  
  1. @interface PodsWebservice : NSObject 
  2. - (void)fetchAllPods:(void (^)(NSArray *pods))callback; 
  3. @end 
这个回调会被每个页面调用。实现这个方法很简单。我们创建一个帮助方法,fetchAllPods:page:,它会为一个页面取得所有的 pods,一旦加载完一页就让它再调用自己。注意一下,为了简洁,我们这里不考虑处理错误,但是你可以在 GitHub 上完整的项目中看到。处理错误总是很重要的,至少打印出错误,这样你可以很快检查到哪些地方没有像预期一样工作:
  
  1. - (void)fetchAllPods:(void (^)(NSArray *pods))callback page:(NSUInteger)page 
  2.     NSString *urlString = [NSString stringWithFormat:@"http://localhost:4567/specs?page=%d", page]; 
  3.     NSURL *url = [NSURL URLWithString:urlString]; 
  4.     [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler: 
  5.       ^(NSData *data, NSURLResponse *response, NSError *error) { 
  6.         id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; 
  7.         if ([result isKindOfClass:[NSDictionary class]]) { 
  8.             NSArray *pods = result[@"result"]; 
  9.             callback(pods); 
  10.             NSNumber* numberOfPages = result[@"number_of_pages"]; 
  11.             NSUInteger nextPage = page + 1; 
  12.             if (nextPage < numberOfPages.unsignedIntegerValue) { 
  13.                 [self fetchAllPods:callback page:nextPage]; 
  14.             } 
  15.         } 
  16.     }] resume]; 
要做的就是这些了。我们解析 JSON,做一些非常粗糙的检查(验证结果是一个字典),然后调用回调函数。
将对象装进 Core Data
现在我们可以将 JSON 装进我们的 Core Data store 中了。为了分清,我们创建一个 Importer 对象来调用 web service,并且创建或者更新对象。将这些放到一个单独的类中很不错,因为这样我们的 web service 和 Core Data 部分完全解耦。如果我们想要给 store 提供一个不同的 web service 或者在别的某个地方重用 web service,我们现在并不需要手动处理这两种情况。同时,不要在 view controller 中编写逻辑代码,以后我们可以在别的 app 中更容易复用这些组件。
我们的 Importer 有两个方法:
  
  1. @interface Importer : NSObject 
  2. - (id)initWithContext:(NSManagedObjectContext *)context  
  3.            webservice:(PodsWebservice *)webservice; 
  4. - (void)import
  5. @end 
通过初始化方法将 context 注入到对象中是一个非常强有力的技巧。当编写测试的时候,我们可以很容易的注入一个不同的 context。同样适用于 web service:我们可以很容易的用一个不同的对象模拟 web service。
import 方法负责处理逻辑。我们调用 fetchAllPods: 方法,并且对于每一批 pod 说明,我们都会将它们导入到 context 中。通过将逻辑代码包装到 performBlock:,context 会确保所有的事情都在正确的线程中执行。然后我们迭代这些说明,并且会为每一个说明生成一个唯一标识符(这些标识符可以是任何独一无二的,只要能确定到唯一一个 model object,正如在 Drew 的文章中解释那样。然后我们试着找到 model object,如果不存在则创建一个。loadFromDictionary: 方法需要一个 JSON 字典,并根据字典中的值更新 model object:
  
  1. - (void)import 
  2.     [self.webservice fetchAllPods:^(NSArray *pods) 
  3.     { 
  4.         [self.context performBlock:^ 
  5.         { 
  6.             for(NSDictionary *podSpec in pods) { 
  7.                 NSString *identifier = [podSpec[@"name"] stringByAppendingString:podSpec[@"version"]]; 
  8.                 Pod *pod = [Pod findOrCreatePodWithIdentifier:identifier inContext:self.context]; 
  9.                 [pod loadFromDictionary:podSpec]; 
  10.             } 
  11.         }]; 
  12.     }]; 
上面的代码中有很多地方要注意。首先,查找或创建方法的效率是非常低下的。在生产环境的代码中,你需要批量处理 pods 并且同时找到他们,正如在《导入大数据集》中「高效地导入数据」这一节中所解释的那样。
第二,我们直接在 Pod 类(managed object 的子类)中创建 loadFromDictionary:。这意味着我们的 model object 知道 web service。在真实的代码中,我们很有可能将这些放到一个类别中,这样这两个很完美的分开了。对于这个示例,这无关要紧。
创建一个独立的后台堆栈
在写上面的代码时,我们会先在在主 managed object context 中拥有一切需要的数据。我们的应用在 table view 控制器中使用一个 fetched results controller 来显示所有的 pods。当 managed object context 中的数据改变时,fetched results controller 自动更新 data model。然而,在主 managed object context 中处理导入数据并不是最优的。主线程可能被堵塞,UI 可能没有反应。大多数时候,在主线程中处理的工作应该是最小限度的,并且造成的延迟应当难以察觉。如果你的情况正是这样,那非常好。然而,如果我们想要做些额外的努力,我们可以在后台线程中处理导入操作。
Apple 在 WWDC 会议以及官方的《Core Data 编程指南》文档的「Concurrency with Core Data」 一节中,对于并发的 Core Data,推荐给开发者两种选择。这两种都需要独立的 managed object contexts,它们要么共享同样的 persistent store coordinator,要么不共享。在处理很多改变时,拥有独立的 persistent store coordinators 提供更出色的性能,因为仅需要的锁只是在 sqlite 级别。拥有共享的 persistent store coordinator 也就意味着拥有共享缓存,当你没有做出很多改变时,这会很快。所以,根据你的情况而定,你需要衡量哪种方案更好,然后选择是否需要一个共享的 persistent store coordinator。当主 context 是只读的情况下,根本不需要锁,因为 iOS 7 中的 sqlite 有写前记录功能并且支持多重读取和单一写入。然而,对于我们的示范目的,我们会使用完全独立堆栈的处理方式。我们使用下面的代码设置一个 managed object context:
  
  1. - (NSManagedObjectContext *)setupManagedObjectContextWithConcurrencyType:(NSManagedObjectContextConcurrencyType)concurrencyType 
  2.     NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:concurrencyType]; 
  3.     managedObjectContext.persistentStoreCoordinator = 
  4.             [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel]; 
  5.     NSError* error; 
  6.     [managedObjectContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType  
  7.                                                                   configuration:nil  
  8.                                                                             URL:self.storeURL  
  9.                                                                         options:nil  
  10.                                                                           error:&error]; 
  11.     if (error) { 
  12.         NSLog(@"error: %@", error.localizedDescription); 
  13.     } 
  14.     return managedObjectContext; 
然后我们调用这个方法两次,一次是为主 managed object context,一次是为后台 managed object context:
  
  1. self.managedObjectContext = [self setupManagedObjectContextWithConcurrencyType:NSMainQueueConcurrencyType]; 
  2. self.backgroundManagedObjectContext = [self setupManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
注意传递的参数 NSPrivateQueueConcurrencyType 告诉 Core Data 创建一个独立队列,这将确保后台 managed object context 的运行发生在一个独立的线程中。
现在就剩一步了:每当后台 context 保存后,我们需要更新主线程。我们在之前第 2 期的这篇文章中描述了如何操作。我们注册一下,当 context 保存时得到一个通知,如果是后台 context,调用 mergeChangesFromContextDidSaveNotification: 方法。这就是我们要做的所有事情:
  
  1. [[NSNotificationCenter defaultCenter] 
  2.         addObserverForName:NSManagedObjectContextDidSaveNotification 
  3.                     object:nil 
  4.                      queue:nil 
  5.                 usingBlock:^(NSNotification* note) { 
  6.     NSManagedObjectContext *moc = self.managedObjectContext; 
  7.     if (note.object != moc) { 
  8.         [moc performBlock:^(){ 
  9.             [moc mergeChangesFromContextDidSaveNotification:note]; 
  10.         }]; 
  11.     } 
  12.  }]; 
这儿还有一个小忠告:mergeChangesFromContextDidSaveNotification: 是在 performBlock:中发生的。在我们这个情况下,moc 是主 managed object context,因此,这将会阻塞主线程。
注意你的 UI(即使是只读的)必须有能力处理对象的改变,或者事件的删除。Brent Simmons 最近写了两篇文章,分别是 《Why Use a Custom Notification for Note Deletion》 和 《Deleting Objects in Core Data》。这些文章解释说明了如何面对这些情况,如果你在你的 UI 中显示一个对象,这个对象有可能会发生改变或者被删除。
实现从 UI 进行的写入
你可能觉得上面讲的看起来非常简单,这是因为仅有的写操作是在后台线程进行的。在我们当前的应用中,我们没有处理其他方面的合并;并没有来自主 managed object context 中的改变。为了增加这个,你可以采用不少策略。Drew 的这篇文章很好的阐述了相关的方法。
根据你的需求,一个非常简单的模式或许是这样:不管用户何时改变 UI 中的某些东西,你并不改变 managed object context。相反,你去调用 web service。如果成功了,你可以从 web service 中得到改变,然后更新你的后台 context。这些改变随后回被传送到主 context。这样做有两个弊端:用户可能需要一段时间才能看到 UI 的改变,并且如果用户未联网,他将不能改变任何东西。在 Florian 的文章中,描述了我们如何使用不同策略让应用在离线时也能工作。
如果你正在处理合并,你也需要定义一个合并原则。这又是根据特定使用情况而定的。如果合并失败了你可能需要抛出一个错误,或者总是给某一个 managed object context 优先权。NSMergePolicy 类描述出了可能的选择。
结论
我们已经看到如何实现一个简单的只读应用,这个应用能将从 web service 取得的大量数据导入到 Core Data。通过使用后台 managed object context,我们已经建立了一个不会阻塞 UI(除非正在处理合并)的 Core Data 程序。

这篇关于Core Data 网络应用实例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1088624

相关文章

swiper实例

大家好,我是燐子,今天给大家带来swiper实例   微信小程序中的 swiper 组件是一种用于创建滑动视图的容器组件,常用于实现图片轮播、广告展示等效果。它通过一系列的子组件 swiper-item 来定义滑动视图的每一个页面。 基本用法   以下是一个简单的 swiper 示例代码:   WXML(页面结构) <swiper autoplay="true" interval="3

Java面试题:通过实例说明内连接、左外连接和右外连接的区别

在 SQL 中,连接(JOIN)用于在多个表之间组合行。最常用的连接类型是内连接(INNER JOIN)、左外连接(LEFT OUTER JOIN)和右外连接(RIGHT OUTER JOIN)。它们的主要区别在于它们如何处理表之间的匹配和不匹配行。下面是每种连接的详细说明和示例。 表示例 假设有两个表:Customers 和 Orders。 Customers CustomerIDCus

BD错误集锦3——ERROR: Can't get master address from ZooKeeper; znode data == null

hbase集群没启动,傻子!   启动集群 [s233 s234 s235]启动zk集群 $>zkServer.sh start $>zkServer.sh status   [s233] 启动dfs系统 $>start-dfs.sh 如果s237 namenode启动失败,则 [s237] $>hadoop-daemon.sh start namenode [s233]启动yarn集群

如何实现一台机器上运行多个MySQL实例?

在一台机器上一个MySQL服务器运行多个MySQL实例有什么好处?这里我先入为主给大家介绍这样做至少存在两个好处(看完这篇文章后理解会更透彻): (1)减轻服务器链接负担 (2)为不同的用户提供不同的mysqld服务器的访问权限以方便这些用户进行自我管理。   下面我介绍具体的实现过程: 一、准备工作     台式机一台、Windows系统、MySQL服务器(我安装的版本是MySQL

Docker Compose--安装Nginx--方法/实例

原文网址:Docker Compose--安装Nginx--方法/实例_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Docker Compose如何安装Nginx。 目录结构 ├── config│   ├── cert│   │   ├── xxx_bundle.pem│   │   └── xxx.key│   ├── conf.d│   └── nginx.co

游戏高度可配置化(一)通用数据引擎(data-e)及其在模块化游戏开发中的应用构想图解

游戏高度可配置化(一)通用数据引擎(data-e)及其在模块化游戏开发中的应用构想图解 码客 卢益贵 ygluu 关键词:游戏策划 可配置化 模块化配置 数据引擎 条件系统 红点系统 一、前言 在插件式模块化软件开发当中,既要模块高度独立(解耦)又要共享模块数据,最好的方法是有个中间平台(中间件)提供标准的接口来进行数据的交换,这在很多行业软件开发中已经广泛应用。但是,由于中间件的抽象和封

用 idea 启动多个实例

在学习负载均衡的时候,要模拟多个实例均提供一个服务,我们要如何用 idea 启动多个实例呢?         如下图,我们已经启动了一个 ProductService 服务,现在想再启动两个相同的服务 1. 选中要启动的服务,右键选择 Copy Configuration... 2 在弹出的框中,选择 Modify options -> Add VM option

简单工厂模式--结合实例学习简单工厂模式

在讲解简单工厂模式之前,有必要先了解一下OO的一些原则  1.OCP(开闭原则,Open-Closed Principle):一个软件的实体应当对扩展开放,对修改关闭。也就是说,对于一个已有的软件,如果需要扩展,应当在不需修改      已有代码的基础上进行。   2.DIP(依赖倒转原则,Dependence Inversion Principle):要针对接口编程,不要针对实现编程。简单点说

Struts2(一)---struts2的环境搭建及实例

刚刚接触struts2,有点懵懵懂懂,还是习惯于先写代码,然后慢慢来理解其中的思想。 这篇文章主要内容是strusts的环境搭建及通过一个简单的例子来理解到底是怎么使用struts来简化编程的。 1.项目结构如下如,包括必须的包 2.web.xml <?xml version="1.0" encoding="UTF-8"?><web-app version="3.0" xmlns="

PINN解偏微分方程实例4

PINN解偏微分方程实例4 一、正问题1. Diffusion equation2. Burgers’ equation3. Allen–Cahn equation4. Wave equation 二、反问题1. Burgers’ equation3. 部分代码示例   本文使用 PINN解偏微分方程实例1中展示的代码求解了以四个具体的偏微分方程,包括Diffusion,Burg