A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始


第一步:搭建 Core Data 多线程环境

这个问题首先要解决的是搭建 Core Data 多线程环境。NSManagedObjectContext 不是线程安全的,你不能随便地开启一个后台线程访问 managed object context 进行数据操作就管这叫支持多线程了。Core Data 对多线程的支持比较好,NSManagedObjectContext 在初始化时可以指定并发模式,有三种选项:

1.NSConfinementConcurrencyType

这种模式是用于向后兼容的,使用这种模式时你应该保证不能在其他线程使用 context,但这点很难保证,不推荐使用。此模式在 iOS 9中已经被废弃。
2.NSPrivateQueueConcurrencyType
在一个私有队列中创建并管理 context。
3.NSMainQueueConcurrencyType
其实这种模式与第 2 种模式比较相似,只不过 context 与主队列绑定,也因此与应用的 event loop 很亲密。当 context 与 UI 更新相关的话就使用这种模式。
搭建多线程 Core Data 环境的方案一般如下,创建一个 NSMainQueueConcurrencyType 的 context 用于响应 UI 事件,其他涉及大量数据操作可能会阻塞 UI 的就使用 NSPrivateQueueConcurrencyType 的 context。环境搭建好了,如何实现多线程操作?官方文档《Using a Private Queue to Support Concurrency》为我们做了示范,在 private queue 的 context 中进行操作时,应该使用以下方法:
func performBlock(_ block: () -> Void)//在私有队列中异步地执行 Blcok
func performBlockAndWait(_ block: () -> Void)//在私有队列中执行 Block 直至操作结束才返回
要在不同线程中使用 managed object context 时,不需要我们创建后台线程然后访问 managed object context 进行操作,而是交给 context 自身绑定的私有队列去处理,我们只需要在上述两个方法的 Block 中执行操作即可。而且,在 NSMainQueueConcurrencyType 的 context 中也应该使用这种方法执行操作,这样可以确保 context 本身在主线程中进行操作。
第二步:数据的同步操作
多 context 同步最简单的方案如下:
NSNotificationCenter.defaultCenter().addObserver(self,
                                             selector: "backgroundContextDidSave:",
                                                 name: NSManagedObjectContextDidSaveNotification,
                                               object: backgroundContext)
func backgroundContextDidSave(notification: NSNotification){
    mainContext.performBlock(){
        mainContext.mergeChangesFromContextDidSaveNotification(notification)
    }
}

NSManagedObjectContext 在执行保存操作后会发出 NSManagedObjectContextDidSaveNotification,包含了 context 所有的变化信息,包括新增的、更新的以及删除的对象的信息;而 mergeChangesFromContextDidSaveNotification(_ notification) 方法则用于合并其他 context 中发生的变化。

如果 context 并未观察其他 context 的 NSManagedObjectContextDidSaveNotification通知,且保存时,persistent store 已经被其他 context 更改过,那么很可能存在差异,此时同步就有了以下几种选择:选择保存 context 中的版本或者使用 persistent store 的版本替换 context 的版本,又或是将两者的版本融合。这种同步方式由 NSManagedObjectContext 的 mergePolicy属性决定。

1.NSErrorMergePolicy

默认策略,有冲突时保存失败,persistent store 和 context 都维持原样,并返回错误信息,是唯一反馈错误信息的合并策略。

2.NSMergeByPropertyStoreTrumpMergePolicy

当 persistent store 和 context 里的版本有冲突,persistent store 里的版本有优先权, context 里使用 persistent store 里的版本替换,但是 context 里没有冲突的变化则不会受到影响。

3.NSMergeByPropertyObjectTrumpMergePolicy

与上面相反,context 里的版本有优先权,persistent store 里使用 context 里的版本替换,但是 persistent store 里没有冲突的变化不受影响。

4.NSOverwriteMergePolicy

用 context 里的版本强制覆盖 persistent store 里的版本。

5.NSRollbackMergePolicy

放弃 context 中的所有变化并使用 persistent store 中的版本进行替换。

同步是件很复杂的事情,实际上还是需要根据实际需要来选择同步方案。上面两种方案中第一种概念简单实现容易,第二种比较复杂相对危险,需要谨慎选择同步策略。还有一点需要注意,如果需要跨线程使用 managed object,那么不要直接在其他 context 里使用该 managed object,而应该通过该对象的 objectID 将该对象 fetch 到 context 里。

最后,搞定大量数据

多线程和同步问题解决,最后的难点:大量数据。大量数据意味着需要我们关注内存占用和性能,写代码时需要记得以下规则:

1.尽可能缓存需要的数据,不相关的数据保持 faults状态。

2.fetch 时尽可能精准,少引入不相关的数据。

3.构建多 context 时尽量将同类 managed object 集中,最大限度减少合并需求。

4.提升操作效率,对Asynchronous Fetch, Batch Update,Batch Delete 等新特性尽可能利用。

多线程编程

在 iOS 编程中,这几种情况下需要处理多线程:UI 事件必须在主线程里进行,其他的可以放在后台进行;而进行一些耗时长或阻塞线程的任务,最后放进后台线程里进行。iOS 的多线程技术有这么几种:线程,GCD 和 NSOperationQueue。线程这种技术比较复杂,而多线程编程向来是「复杂必死」,推荐尽可能使用后二者,但线程有个后二者没有的优势:能够精确保证任务执行的时间。GCD 全称是 Grand Central Dispatch, 是 libdispatch 这个库的外部代号,基于 C 的底层来实现;而NSOperationQueue,通称操作队列,是基于 GCD 实现的。GCD 能做的 NSOperationQueue 基本上都能做,而且还有些 GCD 中不易实现的特性,如挂起、取消任务,虽然在 iOS 8 中,GCD 也提供了取消任务的功能,但在 GCD 中任务的挂起和取消都有较大的局限性;虽然大多数情况下应该使用抽象级别更高的 API,也就是 NSOperationQueue,但处理一般的后台任务我偏爱 GCD,主要是 GCD 搭配 Blcok 使用简单,非常方便。如何选择,下面两个链接对此问题的讨论值得一看:

StackOverflow: NSOperation vs. Grand Central Dispatch

Blog: When to use NSOperation vs. GCD

另外,还推荐这些文章:objc 的并发编程专题《Concurrent Programming》 及中文翻译版本;雷纯锋的博客《iOS 并发编程之 Operation Queues》;NSHipster 的《NSOperation》。

设计模式

评价 Delegate, Notification, KVO 几种设计模式的优缺点

我不觉得这个问题是个好问题,与其比较这几个设计模式的优缺点,不如谈它们各自的特点比较好,因为它们是为了解决某类问题才设计出来的,有各自适合的使用场景。另外,给个 iOS 中设计模式的介绍:iOS Design Patterns。

为什么出题目都喜欢把这三个设计模式拿来对比呢?Notification 和 KVO都是用于协助对象间的通信:某个对象监听某个事件的发生,当某个事件发生时,该对象会得到通知然后做出响应。这几句话大概是以前看过的书本上说的。如果你以前没接触过设计模式,第一次学习时总是能够看到事件、响应这类模糊的词汇,看得你云里雾里,好吧,我说的是我。 但 delegate,应该说没有监听的功能,而是当事件发生或时机到了,要求 delegate 对象做点什么。刚开始学习 OC 的时候,一本书中将 delegate比喻为助手,那时候不怎么理解,现在觉得这个比喻十分恰当。虽然delegate 模式在 OC 中随处可见,在UIViewController 类中广泛存在,但在开发 FaceAlbum 的过程中只遇到过一次自定义 protocol/delegate 的情况,后来还是用 KVO 取代了。相对于 Notification 和 KVO 模式,使用 delegate 模式你会明确知道对象的 delegate 能干什么,因为要成为 某个对象的delegate,该对象得遵守指定的 protocol,protocol 指定了 delegate 对象需要实现的方法。

Notification和 KVO两者都需要监听事件的对象(早期看见事件就犯晕,如今写来觉得用这个词挺顺手的)去注册,delegate则需要 delegate 对象遵守指定的 protocol;Notification 中监听者向一个单例对象NSNotificationCenter注册,NSNotificationCenter类似一个广播中心,接受任何对象的注册,后者则向要监听的对象注册,一对一,这两者都不需要对象之间有联系,而 delegate 则需要通信的对象通过变量联系;NSNotification模式里监听的对象与被监听的对象通信是通过 NSNotificationCenter 这个中介,而KVO 里,不能说两者是直接通信的,我没有了解过过 KVO 是如何实现通信的,从表面上看两者就那么心灵感应一般,这是系统替我们实现的,而delegate,由于通过变量连接,直接向 delegate 发送消息即可,在这点上,NSNotification不需要通信双方知道对方,而后两者则不然;在响应事件时,NSNotification和 KVO 模式里都是在注册时指定响应方法,而 delegate 则在 protocol 里预定义了响应方法。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马