前言

1.NSThread的使用,虽然也可以实现多线程编程,但是需要我们去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销。我们也可以配合使用NSOperation和NSOperationQueue实现多线程编程,实现步骤大致是这样的:

1> 先将需要执行的操作封装到一个NSOperation对象中

2> 然后将NSOperation对象添加到NSOperationQueue中

3> 系统会自动将NSOperation中封装的操作放到一条新线程中执行

在此过程中,我们根本不用考虑线程的生命周期、同步、加锁等问题

下面列举一个应用场景,比如微博的粉丝列表:

每一行的头像肯定要从新浪服务器下载图片后才能显示的,而且是需要异步下载。这时候你就可以把每一行的图片下载操作封装到一个NSOperation对象中,上面有6行,所以要创建6个NSOperation对象,然后添加到NSOperationQueue中,分别下载不同的图片,下载完毕后,回到对应的行将图片显示出来。

2.默认情况下,NSOperation并不具备封装操作的能力,必须使用它的子类,使用NSOperation子类的方式有3种:

1> NSInvocationOperation

2> NSBlockOperation

3> 自定义子类继承NSOperation,实现内部相应的方法

这讲先介绍如何用NSOperation封装一个操作,后面再结合NSOperationQueue来使用。

一、NSInvocationOperation

  1. 1 NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"mj"] autorelease];
  2. 2 [operation start];

* 第1行初始化了一个NSInvocationOperation对象,它是基于一个对象和selector来创建操作

* 第2行调用了start方法,紧接着会马上执行封装好的操作,也就是会调用self的run:方法,并且将@"mj"作为方法参数

* 这里要注意:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作。只有将operation放到一个NSOperationQueue中,才会异步执行操作。

二、NSBlockOperation

1.同步执行一个操作

  1. 1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
  2. 2 NSLog(@"执行了一个新的操作");
  3. 3 }];
  4. 4 // 开始执行任务
  5. 5 [operation start];

* 第1行初始化了一个NSBlockOperation对象,它是用一个Block来封装需要执行的操作

* 第2行调用了start方法,紧接着会马上执行Block中的内容

* 这里还是在当前线程同步执行操作,并没有异步执行

2.并发执行多个操作

  1. 1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
  2. 2   NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);
  3. 3 }];
  4. 4
  5. 5 [operation addExecutionBlock:^() {
  6. 6   NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
  7. 7 }];
  8. 8
  9. 9 [operation addExecutionBlock:^() {
  10. 10   NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
  11. 11 }];
  12. 12
  13. 13 [operation addExecutionBlock:^() {
  14. 14   NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
  15. 15 }];
  16. 16
  17. 17 // 开始执行任务
  18. 18 [operation start];

* 第1行初始化了一个NSBlockOperation对象

* 分别在第5、9、13行通过addExecutionBlock:方法添加了新的操作,包括第1行的操作,一共封装了4个操作

* 在第18行调用start方法后,就会并发地执行这4个操作,也就是会在不同线程中执行

  1. 1 2013-02-02 21:38:46.102 thread[4602:c07] 又执行了1个新的操作,线程:<NSThread: 0x7121d50>{name = (null), num = 1}
  2. 2 2013-02-02 21:38:46.102 thread[4602:3f03] 又执行了1个新的操作,线程:<NSThread: 0x742e1d0>{name = (null), num = 5}
  3. 3 2013-02-02 21:38:46.102 thread[4602:1b03] 执行第1次操作,线程:<NSThread: 0x742de50>{name = (null), num = 3}
  4. 4 2013-02-02 21:38:46.102 thread[4602:1303] 又执行了1个新的操作,线程:<NSThread: 0x7157bf0>{name = (null), num = 4}

可以看出,每个操作所在线程的num值都不一样,说明是不同线程

三、NSOperation的其他用法

1.取消操作

operation开始执行之后, 默认会一直执行操作直到完成,我们也可以调用cancel方法中途取消操作

  1. [operation cancel];

2.在操作完成后做一些事情

如果想在一个NSOperation执行完毕后做一些事情,就调用NSOperation的setCompletionBlock方法来设置想做的事情

  1. operation.completionBlock = ^() {
  2. NSLog(@"执行完毕");
  3. };

当operation封装的操作执行完毕后,就会回调Block里面的内容

四、自定义NSOperation

如果NSInvocationOperation和NSBlockOperation不能满足需求,我们可以直接新建子类继承NSOperation,并添加任何需要执行的操作。如果只是简单地自定义NSOperation,只需要重载-(void)main这个方法,在这个方法里面添加需要执行的操作。

下面写个子类DownloadOperation来下载图片

1.继承NSOperation,重写main方法

DownloadOperation.h

  1. #import <Foundation/Foundation.h>
  2. @protocol DownloadOperationDelegate;
  3.  
  4. @interface DownloadOperation : NSOperation
  5. // 图片的url路径
  6. @property (nonatomic, copy) NSString *imageUrl;
  7. // 代理
  8. @property (nonatomic, assign) id<DownloadOperationDelegate> delegate;
  9.  
  10. - (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate;
  11. @end
  12.  
  13. // 图片下载的协议
  14. @protocol DownloadOperationDelegate <NSObject>
  15. - (void)downloadFinishWithImage:(UIImage *)image;
  16. @end

DownloadOperation.m

  1. 1 #import "DownloadOperation.h"
  2. 2
  3. 3 @implementation DownloadOperation
  4. 4 @synthesize delegate = _delegate;
  5. 5 @synthesize imageUrl = _imageUrl;
  6. 6
  7. 7 // 初始化
  8. 8 - (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate {
  9. 9 if (self = [super init]) {
  10. 10 self.imageUrl = url;
  11. 11 self.delegate = delegate;
  12. 12 }
  13. 13 return self;
  14. 14 }
  15. 15 // 释放内存
  16. 16 - (void)dealloc {
  17. 17 [super dealloc];
  18. 18 [_imageUrl release];
  19. 19 }
  20. 20
  21. 21 // 执行主任务
  22. 22 - (void)main {
  23. 23 // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
  24. 24 @autoreleasepool {
  25. 25 // ....
  26. 26 }
  27. 27 }
  28. 28 @end

* 在第22行重载了main方法,等会就把下载图片的代码写到这个方法中

* 如果这个DownloadOperation是在异步线程中执行操作,也就是说main方法在异步线程调用,那么将无法访问主线程的自动释放池,所以在第24行创建了一个属于当前线程的自动释放池

2.正确响应取消事件

* 默认情况下,一个NSOperation开始执行之后,会一直执行任务到结束,就比如上面的DownloadOperation,默认会执行完main方法中的所有代码。

* NSOperation提供了一个cancel方法,可以取消当前的操作。

* 如果是自定义NSOperation的话,需要手动处理这个取消事件。比如,一旦调用了cancel方法,应该马上终止main方法的执行,并及时回收一些资源。

* 处理取消事件的具体做法是:在main方法中定期地调用isCancelled方法检测操作是否已经被取消,也就是说是否调用了cancel方法,如果返回YES,表示已取消,则立即让main方法返回。

* 以下地方可能需要调用isCancelled方法:

  • 在执行任何实际的工作之前,也就是在main方法的开头。因为取消可能发生在任何时候,甚至在operation执行之前。
  • 执行了一段耗时的操作之后也需要检测操作是否已经被取消
  1. 1 - (void)main {
  2. 2 // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
  3. 3 @autoreleasepool {
  4. 4 if (self.isCancelled) return;
  5. 5
  6. 6 // 获取图片数据
  7. 7 NSURL *url = [NSURL URLWithString:self.imageUrl];
  8. 8 NSData *imageData = [NSData dataWithContentsOfURL:url];
  9. 9
  10. 10 if (self.isCancelled) {
  11. 11 url = nil;
  12. 12 imageData = nil;
  13. 13 return;
  14. 14 }
  15. 15
  16. 16 // 初始化图片
  17. 17 UIImage *image = [UIImage imageWithData:imageData];
  18. 18
  19. 19 if (self.isCancelled) {
  20. 20 image = nil;
  21. 21 return;
  22. 22 }
  23. 23
  24. 24 if ([self.delegate respondsToSelector:@selector(downloadFinishWithImage:)]) {
  25. 25 // 把图片数据传回到主线程
  26. 26 [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO];
  27. 27 }
  28. 28 }
  29. 29 }

* 在第4行main方法的开头就先判断operation有没有被取消。如果被取消了,那就没有必要往下执行了

* 经过第8行下载图片后,在第10行也需要判断操作有没有被取消

* 总之,执行了一段比较耗时的操作之后,都需要判断操作有没有被取消

* 图片下载完毕后,在第26行将图片数据传递给了代理(delegate)对象

多线程编程 NSOperation的更多相关文章

  1. iOS多线程编程--NSOperation(转)

    这篇文章写得非常不错,基础用法都涉及到了,我把文章提到的例子都写到了demo里面, 原文地址: iOS多线程--彻底学会多线程之『NSOperation』 demo下载:https://github. ...

  2. IOS开发 多线程编程 - NSOperation

    一.NSOperation 1.简介 NSOperation实例封装了需要执行的操作和执行操作所需的数据,并且能够以并发或非并发的方式执行这个操作. NSOperation本身是抽象基类,因此必须使用 ...

  3. iOS开发——多线程篇——NSOperation(基于GCD多线程编程),下载图片并合成新图片

    一.NSOperation的基本概念1.简介NSOperation的作用配合使用NSOperation和NSOperationQueue也能实现多线程编程 NSOperation和NSOperatio ...

  4. iOS开发多线程篇—NSOperation简单介绍

    iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...

  5. 用NSOperation和NSOperationQueue实现多线程编程

    1.上一讲简单介绍了NSThread的使用,虽然也可以实现多线程编程,但是需要我们去管理线程的生命周期,还要考虑线程同步.加锁问题,造成一些性能上的开销.我们也可以配合使用NSOperation和NS ...

  6. iOS多线程编程Part 2/3 - NSOperation

    多线程编程Part 1介绍了NSThread以及NSRunLoop,这篇Blog介绍另一种并发编程技术:NSOPeration. NSOperation & NSOperationQueue ...

  7. 多线程编程 (2) -NSOperation

    一.NSInvocationOperation 二.NSBlockOperation 三.NSOperation的其他用法 四.自定义NSOperation 1.上一讲简单介绍了NSThread的使用 ...

  8. iOS多线程编程技术之NSThread、Cocoa NSOperation、GCD

    原文出处: 容芳志的博客 简介iOS有三种多线程编程的技术,分别是:(一)NSThread(二)Cocoa NSOperation(三)GCD(全称:Grand Central Dispatch) 这 ...

  9. IOS高级编程之三:IOS 多线程编程

    多线程的概念在各个操作系统上都会接触到,windows.Linux.mac os等等这些常用的操作系统,都支持多线程的概念. 当然ios中也不例外,但是线程的运行节点可能是我们平常不太注意的. 例如: ...

随机推荐

  1. JAVA代码规范笔记(上)

    本文为<code conventions-150003>(JAVA代码规范)笔记. 文件组织 1.超过2000行代码的源文件将会比较难以阅读,应该避免. 2.每个Java源文件都包含单一的 ...

  2. Request中Attribute 和 Parameter 的区别

    Attribute 和 Parameter 的区别 (1)HttpServletRequest类有setAttribute()方法,而没有setParameter()方法 (2)当两个Web组件之间为 ...

  3. 下载android4.4.2源码全过程(附已下载的源码)

    今天在下载andriod源码,特来与大家分享一下我的经验.当然,网上教下载源码的教程较多,本文主要针对在GFW下下载源码出现的各种问题的解决方法. 1.首先安装下载客户端git , curl. 命令如 ...

  4. Android开发技巧——使用PopupWindow实现弹出菜单

    在本文当中,我将会与大家分享一个封装了PopupWindow实现弹出菜单的类,并说明它的实现与使用. 因对界面的需求,android原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在 ...

  5. (NO.00003)iOS游戏简单的机器人投射游戏成形记(二十)

    接上一篇文章,我们现在来实现篮框的感应器. 所谓感应器,就是在物体接触到的时候做出反应的节点.我们需要将感应器放在篮框底部,这样子弹接触感应器的时候,我们就知道子弹坠入了篮框,从而得分. 为了放置子弹 ...

  6. EXCEPTION与ERROR的区别

    EXCEPTION与ERROR的区别

  7. Windows下配置nginx+FastCgi + Spawn-fcgi

    前提: 下载nginx, FastCgi, Spawn-fcgi Spawn-fcgi有个Windows的版本,但不能在VS中编译,这里有一个编译好的版本:http://download.csdn.n ...

  8. shell的case语句

    case语句格式 # vi test.sh : echo "input : " read num echo "the input data is $num" c ...

  9. jQuery插件AjaxFileUpload文件上传实现Javascript多文件上传功能

     Ajax file upload plugin是一个功能强大的文件上传jQuery插件,可自定义链接.或其它元素庖代传统的file表单上传结果,可实现Ajax动态提示文件上传 过程,同时支撑多文 ...

  10. (二十五)键盘的设置与TextField细节处理

    设置Return Key类型为Send并且勾选下面的复选框即可实现没有内容时禁用keyboard,有内容自动启用. 文本框左边框与文本留有间距的方法: //文本框左侧留下间距 UIView *left ...