iOS开发之多线程技术——NSOperation篇

本篇将从四个方面对iOS开发中使用到的NSOperation技术进行讲解:


一、什么是NSOperation

二、我们为什么使用NSOperation

三、在实际开发中如何使用NSOperation

  1、自定义NSOperation

  2、NSOperation的基本使用

  3、NSOperation实现线程间通信

    1)利用代理进行消息传递

    2)利用通知实现消息传递

    3)利用block进行消息传递

四、与GCD比较


一、什么是NSOperation

  NSOperation是一个抽象的基类,表示一个独立的计算单元,可以为子类提供有用且线程安全的建立状态,优先级,依赖和取消等操作。系统已经给我们封装了NSBlockOperation和NSInvocationOperation这两个实体类。使用起来也非常简单,不过我们更多的使用是自己继承并定制自己的操作。


二、我们为什么使用NSOperation

  在iOS开发中,为了提升用户体验,我们通常会将操作耗时的操作放在主线程之外的线程进行处理。对于正常的简单操作,我们更多的是选择代码更少的GCD,让我们专注于自己的业务逻辑开发。NSOperation在ios4后也基于GCD实现,但是相对于GCD来说可控性更强,并且可以加入操作依赖。


三、在实际开发中如何使用NSOperation

1、自定义NSOperation

在实际开发中,系统提供的NSOperation可能无法满足我们的需求,这时,我们就需要自定义我们自己的NSOperation

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    NSLog(@"touchesBegan------%@", [NSThread currentThread]);

    if (!self.imageView.image) {  // 避免重复下载,增强用户体验

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue setMaxConcurrentOperationCount:6];

        MyOperation *myO = [[MyOperation alloc] init];
        myO.imageView = self.imageView;
        [queue addOperation:myO];

    }

    NSLog(@"end");
}

// 自定义NSOperation
@interface MyOperation : NSOperation

@property (nonatomic, strong) UIImageView *imageView;

@end

// 实现自定义NSOperation
@implementation MyOperation

- (void)main {

    NSLog(@"%s----%@", __func__, [NSThread currentThread]);

    UIImage *image = [self downLoadImage:@"http://g.hiphotos.baidu.com/image/pic/item/f31fbe096b63f624cd2991e98344ebf81b4ca3e0.jpg"];

    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"%@======%@", image, [NSThread currentThread]);
        self.imageView.image = image;
    });
}

- (UIImage *)downLoadImage:(NSString *)urlString {
    NSLog(@"%s----%@",__func__, [NSThread currentThread]);

    NSURL *url = [NSURL URLWithString:urlString];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];

    NSLog(@"图片下载完成");

    return image;
}

@end

2、NSOperation的基本使用

#pragma mark
#pragma mark - NSOperation高级操作1
- (void)highLevelTest1 {
    /**
     NSOperation 相对于 GCD 来说,增加了以下管理线程的功能:
     1.NSOperation可以添加操作依赖:保证操作的执行顺序! --> 和GCD中将任务添加到一个串行队列中是一样的!一个串行队列会对应一条线程
     GCD 中的按顺序执行(串行队列) ---> 串行执行
     添加操作依赖之后,系统有可能串行执行保证任务的执行顺序,还有可能绿色线程同步技术,保证任务执行顺序
     */

    NSInvocationOperation *inO = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];

    NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block1======%@", [NSThread currentThread]);
    }];

    NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block2======%@", [NSThread currentThread]);
    }];

    NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block3======%@", [NSThread currentThread]);
    }];

    /**
     四个操作都是耗时操作,并且要求按顺序执行,操作2是UI操作
     添加操作依赖的注意点
     1.一定要在将操作添加到操作队列中之前添加操作依赖
     2.不要添加循环依赖
     优点:对于不同操作队列中的操作,操作依赖依然有效
     */

    // 1.一定要在将操作添加到操作队列中之前添加操作依赖
    [block2 addDependency:block1];
    [block3 addDependency:block2];
    [inO addDependency:block3];
    // 2.不要添加循环依赖
    [block1 addDependency:block3];

    [[[NSOperationQueue alloc] init] addOperation:block1];
    [[[NSOperationQueue alloc] init] addOperation:block2];
    [[[NSOperationQueue alloc] init] addOperation:block3];

    [[NSOperationQueue mainQueue] addOperation:inO];
}

- (void)test {
    NSLog(@"测试%s-----%@", __func__, [NSThread currentThread]);
}

#pragma mark
#pragma mark - NSOperation高级操作2
- (void)highLevelTest2 {
    /**
     NSOperation高级操作
     应用场景:提高用户体验第一,当用户操作时,取消一切跟用户当前操作无关的进程,提升流畅度
     1.添加操作依赖
     2.管理操作:重点!是操作队列的方法
     2.1暂停/恢复 取消 操作
     2.2开启合适的线程数量!(最多不超过6条)

     一般开发的时候,会将操作队列设置成一个全局的变量(属性)
     */

    NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"---------");
    }];

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    [queue addOperationWithBlock:^{
        [self test];
    }];

    [queue addOperation:block1];

    // 1.暂停操作  开始滚动的时候
    [queue setSuspended:YES];

    // 2.恢复操作  滑动结束的时候
    [queue setSuspended:NO];

    // 3.取消所有操作  接收到内存警告
    [queue cancelAllOperations];

    // 3.1补充:取消单个操作调用该NSOperation的cancel方法
    [block1 cancel];

    // 4.设置线程最大并发数,开启合适的线程数量 实例化操作队列的时候
    [queue setMaxConcurrentOperationCount:6];

    /**
     遇到并发编程,什么时候选择 GCD, 什么时候选择NSOperation
     1.简单的开启线程/回到主线程,选择GCD:效率更高,简单
     2.需要管理操作(考虑到用户交互!)使用NSOperation
     */
}

#pragma mark
#pragma mark - NSOperation简单操作
- (void)BaseTest {
    // 1.实例化操作对象
    NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOperation1---------%@", [NSThread currentThread]);
    }];

    // 往当前操作中追加操作
    [blockOperation1 addExecutionBlock:^{
        NSLog(@"addblockOperation1.1-----%@", [NSThread currentThread]);
    }];

    [blockOperation1 addExecutionBlock:^{
        NSLog(@"addblockOperation1.2-----%@", [NSThread currentThread]);
    }];

    /**
     当 NSBlockOperation中的任务数 > 1 之后,无论是将操作添加到主线程还是在主线程直接执行 start, NSBlockOperation中的任务执行顺序都不确定,执行线程也不确定!
     一般在开发的时候,要避免向 NSBlockOperation 中追加任务!
     如果任务都是在子线程中执行,并且不需要保证执行顺序!可以直接追加任务
     */

    NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOperation2---------%@", [NSThread currentThread]);
    }];

    NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOperation3---------%@", [NSThread currentThread]);
    }];

    // 将操作添加到非主队列中
    //    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //
    //    [queue addOperation:blockOperation1];
    //    [queue addOperation:blockOperation2];
    //    [queue addOperation:blockOperation3];

    // 将操作添加到主队列中
    [[NSOperationQueue mainQueue] addOperation:blockOperation1];
    [[NSOperationQueue mainQueue] addOperation:blockOperation2];
    [[NSOperationQueue mainQueue] addOperation:blockOperation3];
}

3、NSOperation实现线程间通信

1)利用代理进行消息传递

@interface ViewController ()<MyOperationDelegate>

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    NSLog(@"%s-----%@", __func__, [NSThread currentThread]);

    NSString *urlString = @"http://h.hiphotos.baidu.com/image/pic/item/30adcbef76094b366b2389d7a4cc7cd98d109d53.jpg";

    // 1.创建操作对象
    MyOperation *myO = [[MyOperation alloc] init];

    myO.delegate = self;

    // 3.告诉 myO 下载哪一张图片
    myO.urlString = urlString;

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue setMaxConcurrentOperationCount:6];

    // 4.将操作添加到操作队列中
    [queue addOperation:myO];

    NSLog(@"end");
}

#pragma mark
#pragma mark - 实现操作的代理方法
- (void)downImage:(UIImage *)image {
    NSLog(@"%s------%@", __func__, [NSThread currentThread]);
    self.imageView.image = image;
}

@protocol MyOperationDelegate <NSObject>

- (void)downImage:(UIImage *)image;

@end

@interface MyOperation : NSOperation

@property (nonatomic, copy) NSString *urlString;

@property (nonatomic, weak) id<MyOperationDelegate> delegate;

@end

@implementation MyOperation

#pragma mark
#pragma mark - 重写NSOperation的main方法
// 当把自定义的操作添加到操作队列中,或者直接调用操作的 start 方法后,都会自动来执行main 方法中的内容
- (void)main {
    NSLog(@"%s------%@", __func__, [NSThread currentThread]);

    UIImage *image = [self downLoadImageSubThread];

    // 回到主线程执行代理方法
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.delegate respondsToSelector:@selector(downImage:)]) {
            [self.delegate downImage:image];
        }
    });

}

#pragma mark
#pragma mark - 下载网络图片的方法
- (UIImage *)downLoadImageSubThread {

    NSURL *url = [NSURL URLWithString:self.urlString];

    NSData *data = [NSData dataWithContentsOfURL:url];

    NSLog(@"下载完成");
    return [UIImage imageWithData:data];

}

@end

2)利用通知实现消息传递

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIImageView *imageView04;

@end

@implementation ViewController

- (void)viewDidLoad {
    NSLog(@"%s-----%@", __func__, [NSThread currentThread]);
    // 注册通知观察者
    // object:nil ,nil可以保证观察者接收任意类型的通知!
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setUpImageWithNotify:) name:@"FinishDownLoadImage" object:nil];
}

- (void)dealloc {
    // 通知用完之后需要移除
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

// 接收到通知之后,执行的方法 noti:接收到的通知
- (void)setUpImageWithNotify:(NSNotification *)noti {
    NSLog(@"%s-----%@", __func__, [NSThread currentThread]);
    // 显示图片
    self.imageView04.image = noti.object;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    NSLog(@"%s-----%@", __func__, [NSThread currentThread]);

    // 1.创建操作队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue setMaxConcurrentOperationCount:6];

    // 2.告诉操作下载哪张图片
    MyOperation *myO = [[MyOperation alloc] init];

    myO.urlString = @"http://h.hiphotos.baidu.com/image/pic/item/72f082025aafa40f7c884d31af64034f79f0198b.jpg";

    // 3.将操作添加到操作队列,会自动调用操作中的main 方法
    [queue addOperation:myO];

    NSLog(@"end");
}

/**************************************************/
// 自定义NSOperation类
@interface MyOperation : NSOperation

@property (nonatomic, copy) NSString *urlString;

@end

// 实现自定义NSOperation类
@implementation MyOperation

- (void)main {
    NSLog(@"%s-----%@", __func__, [NSThread currentThread]);

    // 在子线程下载好图片后再传给主线程
    UIImage *image = [self downLoadImage:self.urlString];

    // 图片下载完毕之后,利用通知告诉控制器,图片下载结束,并且将下载好的图片传递给控制器
    // 在主线程发送通知
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"FinishDownLoadImage" object:image];
    });

}

- (UIImage *)downLoadImage:(NSString *)urlString {

    NSURL *url = [NSURL URLWithString:urlString];

    NSData *data = [NSData dataWithContentsOfURL:url];

    return [UIImage imageWithData:data];

}

3)利用block进行消息传递

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    NSLog(@"%s----%@",__func__, [NSThread currentThread]);

    NSString *urlString = @"http://h.hiphotos.baidu.com/image/pic/item/72f082025aafa40f7c884d31af64034f79f0198b.jpg";

    // 2.创建操作
    MyOperation *mo = [[MyOperation alloc] init];
    mo.urlString = urlString;

    mo.view = self.view;

    [mo downLoadWebImageWithBlock:^(UIImage *image) {
        self.imageView.image = image;
    }];

    [mo setCompletionBlock:^{
        NSLog(@"图片下载完成");
    }];

    NSLog(@"end");

}

/*********************************************************/
// 自定义NSOperation类
typedef void(^downLoadBlock)(UIImage *image);

@interface MyOperation : NSOperation

@property (nonatomic, strong) UIView *view;

@property (nonatomic, copy) NSString *urlString;

@property (nonatomic, copy) downLoadBlock block;

- (void)downLoadWebImageWithBlock:(downLoadBlock)blk;

@end

// 实现自定义NSOperation类
@implementation MyOperation

#pragma mark
#pragma mark - 实现block方法,方便直接回车调用
- (void)downLoadWebImageWithBlock:(downLoadBlock)blk {
    if (blk) {
        self.block = blk;

        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            UIImage *image = [self downLoadImage:self.urlString];

            dispatch_async(dispatch_get_main_queue(), ^{

                if (self.block) {
                    self.block(image);
                }

            });
        });
    }

}

#pragma mark
#pragma mark - 下载图片的方法
- (UIImage *)downLoadImage:(NSString *)strUrl {

    NSLog(@"%s----%@",__func__, [NSThread currentThread]);

    NSURL *url = [NSURL URLWithString:strUrl];

    NSData *data = [NSData dataWithContentsOfURL:url];

    NSLog(@"下载完成");

    return [UIImage imageWithData:data];

}

@end

四、与GCD比较

GCD:

将任务(block)添加到队列(串行/并发/主队列),并且指定任务执行的函数(同步/异步)
GCD是底层的C语言构成的API
iOS 4.0 推出的,针对多核处理器的并发技术
在队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构
要停止已经加入 queue 的 block 需要写复杂的代码
需要通过 Barrier 或者同步任务设置任务之间的依赖关系
只能设置队列的优先级
高级功能:
一次性 once
延迟操作 after
调度组

NSOperation:

核心概念:把操作(异步)添加到队列(全局的并发队列)
OC 框架,更加面向对象,是对 GCD 的封装
iOS 2.0 推出的,苹果推出 GCD 之后,对 NSOperation 的底层全部重写
Operation作为一个对象,为我们提供了更多的选择
可以随时取消已经设定要准备执行的任务,已经执行的除外
可以跨队列设置操作的依赖关系
可以设置队列中每一个操作的优先级
高级功能:
最大操作并发数(GCD不好做)
继续/暂停/全部取消
跨队列设置操作的依赖关系

时间: 2024-10-15 01:07:53

iOS开发之多线程技术——NSOperation篇的相关文章

iOS开发之多线程技术——GCD篇

本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一.什么是GCD 二.我们为什么要用GCD技术 三.在实际开发中如何使用GCD更好的实现我们的需求 一.Synchronous & Asynchronous 同步 & 异步 二.Serial Queues & Concurrent Queues 串行 & 并发 三.Global Queues全局队列 四.Main Queue主队列 五.同步的作用 六.dispatch_time延迟操作 七.线程安全(单例dispa

iOS开发——网络使用技术OC篇&amp;网络爬虫-使用正则表达式抓取网络数据

网络爬虫-使用正则表达式抓取网络数据 关于网络数据抓取不仅仅在iOS开发中有,其他开发中也有,也叫网络爬虫,大致分为两种方式实现 1:正则表达 2:利用其他语言的工具包:java/Python 先来看看网络爬虫的基本原理: 一个通用的网络爬虫的框架如图所示: 网络爬虫的基本工作流程如下: 1.首先选取一部分精心挑选的种子URL: 2.将这些URL放入待抓取URL队列: 3.从待抓取URL队列中取出待抓取在URL,解析DNS,并且得到主机的ip,并将URL对应的网页下载下来,存储进已下载网页库中.

【iOS开发】多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用

http://blog.csdn.net/crycheng/article/details/21799611 本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSInvocationOperation.NSOperationQueue的使用,列举几个简单的例子. 默认情况下,NSOperation并不具备封装操作的能力,必须使用它的子类,使用NSOperation子类的方式有3种: 1> 自定义子类继承NSOperation,实现内部相应的方法 2> NSB

iOS开发之多线程技术(NSThread、OperationQueue、GCD)

在前面的博客中如果用到了异步请求的话,也是用到的第三方的东西,没有正儿八经的用过iOS中多线程的东西.其实多线程的东西还是蛮重要的,如果对于之前学过操作系统的小伙伴来说,理解多线程的东西还是比较容易的,今天就做一个小的demo来详细的了解一下iOS中的多线程的东西.可能下面的东西会比较枯燥,但还是比较实用的. 多线程用的还是比较多的,废话少说了,下面的两张截图是今天我们实验的最终结果,应该是比较全的,小伙伴们由图来分析具体的功能吧: 功能说明: 1.点击同步请求图片,观察整个UI界面的变化,并点

iOS开发:多线程技术概述

一.概述 线程(thread):用于指代独立执行的代码段. 进程(process):用于指代一个正在运行的可执行程序,它可以包含多个线程. 任务(task):用于指代抽象的概念,表示需要执行工作. 多线程的替代方法: Operation objects(操作对象):操作对象可能创建线程更快,因为它们使用内核里面常驻线程池里面的线程来节省创建的时间,而不是每次都创建新的线程. Grand Central Dispatch(GCD):如果你更关注你任务的完成而不是线程的管理,那么GCD是很好的选择,

iOS开发——项目实战技术OC篇&amp;XMPP简单总结

XMPP简单总结 最近面试被问到了一个问题,笔者当时就懵了:什么XMPP,平时怎么使用,使用过程中遇到什么问题?. 但是还是通过记忆,简单的说了一下自己所知道了,不过那并没有撒卵用,所以你懂的 XMPPFramework是一个OS X/iOS平台的开源项目,使用Objective-C实现了XMPP协议(RFC-3920),同时还提供了用于读写XML的工具,大大简化了基于XMPP的通信应用的开发. 1.关于连接的 1 //此方法在stream开始连接服务器的时候调用 2 - (void)xmppS

IOS开发-多线程编程技术(Thread、Cocoa operations、GCD)

前言:在软件开发中,多线程编程技术被广泛应用,相信多线程任务对我们来说已经不再陌生了.有了多线程技术,我们可以同做多个事情,而不是一个一个任务地进行.比如:前端和后台作交互.大任务(需要耗费一定的时间和资源)等等.也就是说,我们可以使用线程把占据时间长的任务放到后台中处理,而不影响到用户的使用. 线程的定义: 每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程.进程也可能是整个程序或者是部分程序的动态执行.线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行.也可以把它理

iOS多线程技术—NSOperation用法

iOS多线程技术—NSOperation用法 一.NSOperation简介 1.简单说明 NSOperation的作?:配合使用NSOperation和NSOperationQueue也能实现多线程编程 NSOperation和NSOperationQueue实现多线程的具体步骤: (1)先将需要执行的操作封装到一个NSOperation对象中 (2)然后将NSOperation对象添加到NSOperationQueue中 (3)系统会?动将NSOperationQueue中的NSOperat

学习IOS开发网络多线程篇--NSThread/GCD/

NSThread:利用NSThread创建和启用一个线程 1. NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];,调用后调用[thread start]; 2. 创建线程后自动启动线程 ,[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; 3. 隐式创建