zoukankan      html  css  js  c++  java
  • iOS多线程--NSOperation 浅显易懂

    NSOperation是基于GCD的一套多线程实现方案,和GCD一样,线程的生命周期是由系统来自动管理的,不用像NSThread和Pthread一样让程序员手动管理。相对于GCD来说,它更加地面向对象,并且比GCD多了一些更加简单实用的功能,另外,由于它的API是纯OC的,深受广大程序员喜爱,实用频率很高。

    NSOperation主要和NSOperationQueue配合使用实现多线程,一般步骤如下:

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

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

    3.系统会自动将NSOperationQueue中的NSOperation取出来;

    4.将取出的NSOperation放到一条线程中执行。

    注意:NSOperation是一个抽象类,并不具备封装操作的能力,必须使用它的子类。使用NSOperation子类的方式有如下三种:

    1.NSInvocationOperation

    2.NSBlockOperation

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

    我们首先来看前两种:

    一、NSOperation:

    复制代码
     1 - (void)viewDidLoad {
     2     [super viewDidLoad];
     3     [self invocationOperation];
     4 }
     5 
     6 -(void)invocationOperation
     7 {
     8     
     9     // 1.创建操作对象,封住需要执行的任务
    10     NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
    11     
    12     // 2.执行操作(默认情况下,如果操作没有放到操作队列中,同步执行)
    13     [operation start];
    14 }
    15 
    16 -(void)download1
    17 {
    18     NSLog(@"下载1----%@",[NSThread currentThread]);
    19 }
    复制代码

    结果如下:

    可以看出,默认情况下,如果操作没有放到操作队列中,会在主线程同步执行,只有讲NSOperation放到NSOperationQueue中才会异步执行。

     二、NSBlockOperation

    复制代码
     1 - (void)viewDidLoad {
     2     [super viewDidLoad];
     3     [self blockOperation];
     4 }
     5 
     6 - (void)blockOperation
     7 {
     8     NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
     9         NSLog(@"NSBlockOperation--下载1---%@",[NSThread currentThread]);
    10     }];
    11     
    12     [operation addExecutionBlock:^{
    13         NSLog(@"NSBlockOperation--下载2---%@",[NSThread currentThread]);
    14     }];
    15     
    16     [operation addExecutionBlock:^{
    17         NSLog(@"NSBlockOperation--下载3---%@",[NSThread currentThread]);
    18     }];
    19     [operation start];
    20 }
    复制代码

    结果如图:

    我们会发现,NSBlockOperation当只有单个任务的时候,也是默认在主线程执行,当任务数大于1的时候,会开启子线程并发执行其它的操作

    三、配合使用NSOperationQueue

    复制代码
     1 - (void)viewDidLoad {
     2     [super viewDidLoad];
     3     [self operationQueue];
     4 }
     5 
     6 - (void)operationQueue
     7 {
     8     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
     9     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
    10     NSInvocationOperation *operation3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download3) object:nil];
    11     NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
    12         NSLog(@"NSBlockOperation--1---%@",[NSThread currentThread]);
    13     }];
    14     [operation4 addExecutionBlock:^{
    15         NSLog(@"NSBlockOperation--2---%@",[NSThread currentThread]);
    16     }];
    17     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    18     [queue addOperation:operation1];
    19     [queue addOperation:operation2];
    20     [queue addOperation:operation3];
    21     [queue addOperation:operation4];
    22 }
    23 -(void)download1
    24 {
    25     NSLog(@"下载1----%@",[NSThread currentThread]);
    26 }
    27 -(void)download2
    28 {
    29     NSLog(@"下载2----%@",[NSThread currentThread]);
    30 }
    31 -(void)download3
    32 {
    33     NSLog(@"下载3----%@",[NSThread currentThread]);
    34 }
    复制代码

    结果如下

    可以看出,只要是添加到NSOperationQueue中的操作,系统都会自动为我们开启子线程来执行,并且是并发无序的执行,和添加的顺序无关。

    然而有些时候我们需要明确地指定操作的顺序,我们可以在NSOperation之间设置依赖来保证执行顺序。

    比如操作1要在操作2后面执行,可以这么写:[operation1 addDependency:operation2] 意思是operation1依赖于operation2,也就是operation1要在operation2后面执行

    上代码验证下:

    复制代码
     1 - (void)viewDidLoad {
     2     [super viewDidLoad];
     3     [self operationQueue];
     4 }
     5 
     6 - (void)operationQueue
     7 {
     8     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
     9     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
    10     NSInvocationOperation *operation3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download3) object:nil];
    11     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    12     [operation3 addDependency:operation1];
    13     [operation1 addDependency:operation2];
    14     [queue addOperation:operation1];
    15     [queue addOperation:operation2];
    16     [queue addOperation:operation3];
    17 }
    18 -(void)download1
    19 {
    20     NSLog(@"下载1----%@",[NSThread currentThread]);
    21 }
    22 -(void)download2
    23 {
    24     NSLog(@"下载2----%@",[NSThread currentThread]);
    25 }
    26 -(void)download3
    27 {
    28     NSLog(@"下载3----%@",[NSThread currentThread]);
    29 }
    复制代码

    结果如下:

    操作顺序Operation2->Operation1->Operation3,完全正确,其实就是谁依赖谁,谁就在它的后面执行

     另外也可以在不同queue的NSOperation之间设置依赖

    操作顺序:4->3->2->1 其余两个并发执行

    下面列出了NSOperationQueue的一些属性和方法:

    1.- (void)cancelAllOperations 取消队列的所有操作。PS:也可以调用NSOperation的-(void)cancel方法取消单个操作

    2.@property (getter=isSuspended) BOOL suspended;YES表示暂停队列,NO表示回复队列

    3.@property NSInteger maxConcurrentOperationCount;表示最大并发数,一般不大于5

    四、自定义NSOperation

     当将自定义的NSOperation添加到NSOperationQueue的时候,系统会调用NSOperation的-(void)main方法,所以我们只要重写这个方法实现相应的实现即可。

    复制代码
     1 -(void)main
     2 {
     3     @autoreleasepool { // 异步线程无法访问主线程的自动释放池,需要自己添加
     4         if (self.isCancelled) return;// 时刻监听操作是否取消,若取消就返回
     5         NSURL *url = [NSURL URLWithString:self.matchUrl];
     6         if (self.isCancelled) return;
     7         NSData *data = [NSData dataWithContentsOfURL:url];
     8         if (self.isCancelled) return;
     9         UIImage *image = [UIImage imageWithData:data];
    10         if ([_delegate respondsToSelector:@selector(operation:finshedDownloadImage:)]) {
    11             dispatch_async(dispatch_get_main_queue(), ^{// 在主线程更新UI
    12                 [self.delegate operation:self finshedDownloadImage:image];
    13             });
    14         }
    15     }
    16 }
    复制代码

    注意由于主线称无法访问主线程的自动释放池,所以需要自己添加。

    下面是个小Demo截图,自定义NSOperation实现图片的异步下载,以及避免重复下载同一张图片

  • 相关阅读:
    elasticsearch官方文档摸索
    nginx报错upstream sent invalid chunked response while reading upstream
    LRU算法的实现
    linux命令小计
    【阅读笔记】深入java虚拟机-第三部分-虚拟机执行子系统
    spring-session-data-redis导致跨域session失效
    ReentrantLock源码解读
    AbstractQueuedSynchronizer(AQS源码解读)
    Object中wait()、notify()、notifyAll()
    redis(单机模式)分布式锁的实现【已废弃】
  • 原文地址:https://www.cnblogs.com/worldtraveler/p/4709863.html
Copyright © 2011-2022 走看看