每个人在使用 iOS 或 Mac 应用时都有过令人沮丧的经历 - 当点击按钮或输入文本时,用户界面停止响应。
幸运 - 您盯着一段时间的沙漏型或五颜六色的车轮旋转,直到您能再次进行交互!让人不愉快,不是吗?
在 iOS 应用中,用户期待您的应用立即回应他们的触动。当他们感觉到应用缓慢,通常会导致不好的评论。
然而,这是说起来容易,做起来难。一旦您的应用程序需要一次执行更多的任务,事情变得复杂。在保持用户界面响应性的同时没有太多的时间在主运行循环来完成繁重的工作。
那么开发者要怎么做呢?一个解决方案是通过并发将工作任务移离主线程。并发意味者您的应用同时执行多个流(或线程),这样当您的工作正在执行时用户界面仍然可以保持可响应。
可以使用 iOS 提供的 NSOperation 与 NSOperationQueue 类来实现并发操作。 在本教程中,您将学习如何使用它们!您将先创建一个不使用多线程的应用来看看它的体验有多糟,接着您将使用并发操作重写这个应用,希望能提供更好的用户体验!
在阅读本教程之前,阅读我们的 iOS 多线程和大量集中调度入门教程可能对您更有帮助。然而,本教程是自给自足的,所以阅读它并不是必需的。
在您开始本教程之前,需要了解几个技术概念。
这很可能是你听说过的并发和并行操作。从技术角度来说,并发是一个程序属性,并行是一个机器属性。并发与并行是两个不同的概念。对于一个开发者来说,您永远不能假定您的代码将运行在一台能够在并行操作中处理您的代码的机器上。然而,您能让您的代码充分利用并行操作。
首先,必须定义一些术语:
注意: 在 iPhone 与 Mac 中,多线程功能由 POSIX 线程 API (或 pthreads) 支持,它是操作系统的一部分。 这是相当低层的东西,您会发现很容易犯错误; 最糟糕的事情是有关线程的这些错误令人难以置信的很难定位!
Foundation 框架包含了一个名为 NSThread 类,它相对简单,但是使用它来管理多线程仍然是件头疼的事。NSOperation 和 NSOperationQueue 是高层类,极大地简化了多线程的处理。
在这个图表中,您可以看到进程,线程与任务之前的关系:
进程,线程与任务
正如您看到的,一个进程可以包括多个执行线程,每个线程同时执行一个任务。
在这个图表中,当线程 1 执行用户界面相关代码时,线程 2 执行读取文件的工作。 这个和您如何在 iOS 构造自己的代码是相似的 - 主线程执行所有与用户界面相关的工作,第二线程执行相对耗时的操作。(如读取文件,访问网络等)
你可能听说过Grand Central Dispatch (GCD)。总而言之,GCD是由语言特性,运行时库和系统增强包所提供的系统的和综合的提升,以支持在iOS和OS X上多核硬件上的并发。如果你想学习更多关于GCD的内容,你可以阅读我们的Multithreading and Grand Central Dispatch on iOS for Beginners Tutorial。
在Mac OS X v10.6和iOS 4之前,NSOperation与NSOperationQueue是不同于GCD的,采用的是两种不同的机制。从Mac OS X v10.6和iOS 4开始,NSOperation与NSOperationQueue是在GCD之上构建的。作为一个非常普遍的规则,Apple建议使用最高级别的抽象,然后当测量结果表明需要时才下降到较低层次的API。
这是两者的一个简单比较,能帮助你决定什么时候以及在哪些地方使用GCD或NSOperation与NSOperationQueue:
GCD是一种轻量级方式来描绘用以并发执行的工作单元。你不用计划这些工作单元;系统会为你计划好。在blocks中添加信赖会是一件头痛的事。作为一个开发者,取消或暂停一个代码块需要额处的工作量!
NSOperation和NSOperationQueue相比于GCD,增加了一点额外开销,但你可以在不同的操作之间添加信赖。你可以重用操作,取消或挂起它们。NSOperation适合Key-Value Observation (KVO);举例来说,你可以通过对NSNotificationCenter的监听来使一个NSOperation开始运行。
在项目的初始模型中,你有一个以字典作为数据源的表视图。字典的键是图片的名字,每一个键的值则是相应图片所处位置的URL。这个项目的目的是从字典中读取它的内容,下载图片,应用图片滤镜,并最终将图片显示在表视图中。
以下是这个模型的结构示意图:
初始模型
其它翻译版本 (1) 加载中注意: 如果您不想自己创建这个项目的无线程版本,直接开始多线程部分,您可以下载本节完成后的项目第一版本跳过本节。
所有图片来源于 stock.xchng。 在数据源中有些图片源是故意的错误,以重现图片下载失败的行为。
启动 Xcode ,选择 iOSApplicationEmpty Application 模版,点击 Next。项目名为 ClassicPhotos。选择 Universal, 选中 Use Automatic Reference Counting (取消其它复选项的选中状态) 继续点击 Next。 在任何您希望的位置保存项目。
其它翻译版本 (1) 加载中从项目导航中选择ClassicPhoto项目。选择Targets ClassicPhotosBuild Phases 并展开Link Binary with Libraries。使用按钮+来添加Core Image framework(你需要Core Image来过滤图片)。
在项目导航里转换到AppDelegate.h文件,然后导入ListViewController——这个打算作为根视图控制器,你将在之后声明。ListViewController会是UITableViewController的一个子类。
#import "ListViewController.h"
转换到 AppDelegate.m,找到 application:didFinishLaunchingWithOptions:。初始化并分配一个ListViewController实例。把它放入到UINavigationController的一个实例中,并将该实例设置为UIWindow的 根视图控制器。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; /* ListViewController is a subclass of UITableViewController. We will display images in ListViewController. Here, we wrap our ListViewController in a UINavigationController, and set it as the root view controller. */ ListViewController *listViewController = [[ListViewController alloc] initWithStyle:UITableViewStylePlain]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:listViewController]; self.window.rootViewController = navController; [self.window makeKeyAndVisible]; return YES; }注意:如果你之前没有使用这种方式创建过一个用户接口,这就是你如何不使用Storyboards或Interface Builder而是以代码地方式创建一个用户接口。我们这样做只是为了这篇教程的简便性。 本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务