博客> iOS开发之多线程NSOperation
iOS开发之多线程NSOperation
2019-12-11 21:23 评论:0 阅读:997 孤雁南飞
ios

前言 这篇文章主要讲NSOperation的使用,相关博客如下: iOS开发之多线程理论部分 GCD NSThread

What 使用NSOperation和NSOperationQueue进行多线程开发类似于线程池,只要将一个NSOperation(实际开发中需要使用其子类NSInvocationOperation、NSBlockOperation)放到NSOperationQueue这个队列中线程就会依次启动。NSOperationQueue负责管理、执行所有的NSOperation,在这个过程中可以更加容易管理线程总数和控制线程之间的依赖关系。

NSOperation 利用他来创建线程操作,线程操作只有放在线程队列中才会在子线程中执行。

NSOperationQueue: 线程队列分两种类型。

主队列 [NSOperationQueue mainQueue] 凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行。 非主队列 [[NSOperationQueue alloc]init] 添加到这种队列中的任务,都会放到子线程中执行。 NSOperation常用子类用于创建线程操作:NSInvocationOperation和NSBlockOperation,两种方式本质没有区别,但后者使用block形式进行代码组织,使用相对方便。也可以用自定义的继承于NSOperation的类来创建线程操作。

这篇文章主要讲NSThread的使用,更多关于多线程理论请移步iOS开发之多线程理论部分,关于使用NSThread创建多线程请移步iOS开发之多线程NSThread

How 配合使用NSOperation和NSOperationQueue实现多线程编程,一共有三种方式,但其实这三种方式都是采用NSOperation的子类与NSOperationQueue搭配实现多线程开发。这三个子类分别是NSInvocationOperation、NSBlockOperation和自定义继承于NSOperation的类,前两者是系统提供的子类。

方式一 NSInvocationOperation与NSOperationQueue搭配

创建一个线程操作,并实现方法选择器选择的方法

//创建一个线程操作 NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(invocationOperation:) object:kurl];

//让线程操作开始执行。但是如果这样做的话这个操作将会在主线程中执行,只有将这个操作放进队列,才会开辟一个子线程让这个操作在子线程中执行。 //[invocationOperation start]; 创建一个线程队列

NSOperationQueue *operationQueue = [NSOperationQueue new]; 将创建好的线程操作放在线程队列中

//只有放在线程队列中的线程操作才会在子线程中执行。线程队列负责管理、执行所有的NSOperation [operationQueue addOperation:invocationOperation]; 在创建线程操作时选择的方法内更新UI

  • (void)invocationOperation:(NSString *)url{ //虽然没有采用NSThread创建线程,但仍可以使用[NSThread currentThread]来获取当前的线程。 NSLog(@"invocationOperation方法所在的线程%@",[NSThread currentThread]);

    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];

    //在子线程中回到主线程更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{

    imageView.image = [UIImage imageWithData:data];

    }];

} 到目前为止是不是感觉跟NSThread挺相似的,接下来简单分析一下两者的区别。

NSThread需要启动,也就是说需要费心管理声明周期,而采用Operation只需将线程操作放到线程队列中即可,线程队列负责管理、执行所有的NSOperation。

管理线程的最大并发数,也就是同时执行的任务数。

//默认是-1,不能设为0,如果设置为0就不执行任务。 operationQueue.maxConcurrentOperationCount = 1; 控制线程之间的依赖关系,NSOperation之间可以设置依赖来保证执行顺序,比如一定要让操作1执行完后,才能执行操作2。线程之间不能相互依赖,不如A依赖于B,B有依赖于A。

//操作1依赖于操作2 [invocationOperation1 addDependency:invocationOperation2]; 队列的取消、暂停、恢复

只要设置队列的suspended为YES, 那么就会暂停队列中其它任务的执行,也就是说不会再继续执行没有执行到得任务

self.queue.suspended = YES; 注意: 设置为暂停之后, 不会立即暂停,会继续执行当前正在执行的任务, 直到当前任务执行完毕, 就不会执行下一个任务了,也就是说, 暂停其实是暂停下一个任务, 而不能暂停当前任务 注意: 暂停是可以恢复的,只要设置队列的suspended为NO, 那么就会恢复队列中其它任务的执行

取消队列中所有的任务的执行

[self.queue cancelAllOperations]; 取消和暂停一样, 是取消后面的任务, 不能取消当前正在执行的任务,取消是不可以恢复的

方式二 NSBlockOperation与NSOperationQueue搭配,其实方式一和方式二没有什么本质区别。主要是后者使用block形式进行代码组织,使用相对方便。

  • (void)viewDidLoad{ [super viewDidLoad];

    imageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
    [self.view addSubview:imageView];
    
    //1. 创建线程操作
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
    
        NSLog(@"blockOperation线程操作所在的线程%@",[NSThread currentThread]);
    
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];
    
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    
        NSLog(@"更新UI所在的线程%@",[NSThread currentThread]);
    
        imageView.image = [UIImage imageWithData:data];
    
        }];
    }];
    //2. 创建线程队列
    NSOperationQueue *operationQueue = [NSOperationQueue new];
    //3. 将线程操作放到线程队列中
    [operationQueue addOperation:blockOperation];
    
    //PS: 简化以上操作
    //1. 创建一个线程队列
    NSOperationQueue *operationQueue = [NSOperationQueue new];

    //直接利用线程队列的addOperationWithBlock添加线程操作。 [operationQueue addOperationWithBlock:^{

      NSLog(@"更新UI所在的线程%@",[NSThread currentThread]);

    }];

} 方式三 继承于NSOperation的子类与NSOperationQueue的搭配

创建一个继承于NSOperation的类,并在.m文件中重写main方法,main方法便是该线程要执行的操作。注意,如果是同步操作,该方法能够自动访问到主线程的自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池,需要再main中再新建一个自动释放池,来帮助管理内存。

创建线程队列,并把线程操作放在线程队列中。

.h // // CoustomOperation.h // NSOperation // // Created by GG on 16/2/26. // Copyright © 2016年 GG. All rights reserved. //

import

import

@interface CoustomOperation : NSOperation

//接收传进来的图片对象 @property (nonatomic,retain) UIImageView *imageView;

//在该该类对象初始化时,将图片试图对象传到类中。

  • (instancetype)initWithImageView:(UIImageView *)imageView;

@end .m // // CoustomOperation.m // NSOperation // // Created by GG on 16/2/26. // Copyright © 2016年 GG. All rights reserved. //

import "CoustomOperation.h"

define kurl @"http://store.storeimages.cdn-apple.com/8748/as-images.apple.com/is/image/AppleInc/aos/published/images/s/38/s38ga/rdgd/s38ga-rdgd-sel-201601?wid=848&hei=848&fmt=jpeg&qlt=80&op_sharpen=0&resMode=bicub&op_usm=0.5,0.5,0,0&iccEmbed=0&layer=comp&.v=1454777389943"

@implementation CoustomOperation

  • (instancetype)initWithImageView:(UIImageView *)imageView { self = [super init]; if (self) {

    self.imageView = imageView;

    } return self; }

  • (void)main{

    //新建一个自动释放池,因为如果是同步操作,该方法能够自动访问到主线程的自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池。 @autoreleasepool {

         NSLog(@"获取图片所在的线程%@",[NSThread currentThread]);
         NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:kurl]];
    
         [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    
         NSLog(@"更新UI所在的线程%@",[NSThread currentThread]);
    
         self.imageView.image = [UIImage imageWithData:imageData];
    
         }];

    } }

@end ViewController.m

  • (void)viewDidLoad{ [super viewDidLoad];

    imageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)]; [self.view addSubview:imageView];

    CoustomOperation coustomOperation = [[CoustomOperation alloc] initWithImageView:(UIImageView )imageView];

    NSOperationQueue *operationQueue = [NSOperationQueue new]; // [operationQueue addOperation:coustomOperation];

} 总结: 这三种方式中,感觉方式一是最麻烦的,方式二相对而已简洁不少,而方式三更适合于封装某一个线程操作。在我下面提供的demo示例中实现了两种功能,欢迎参考。 是用NSOperation加载一张图片 使用NSOperation加载多张图片,并能使线程暂停,恢复,终止,

收藏
0
sina weixin mail 回到顶部