iOS-多线程之NSOperation

NSOperation介绍

NSOperation 是GCD 的封装,相比于GCD添加了额外的一些操作,比如取消任务、添加任务依赖关系。不需要关心线程生命周期。
NSOperation是一个抽象基类,使用比较多的就是它的子类NSInvocationOperation和NSBlockOperation。或者自定义NSOperation的子类。
NSOperation 常用属性和方法 解释
dependencies 在当前操作开始执行之前完成执行的所有操作对象数组
-(void)cancel 可取消操作,实质是标记 isCancelled 状态
-(BOOL)isFinished 判断操作是否已经结束
-(BOOL)isCancelled 判断操作是否已经标记为取消
-(BOOL)isExecuting 判断操作是否正在在运行
-(BOOL)isReady 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关
-(void)waitUntilFinished 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步
-(void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock
-(void)addDependency:(NSOperation *)op 添加依赖,使当前操作依赖于操作 op 的完成
-(void)removeDependency:(NSOperation *)op 移除依赖,取消当前操作对操作 op 的依赖
-(NSOperationQueuePriority)queuePriority 获取优先级
-(void)setQueuePriority:(NSOperationQueuePriority)p 设置优先级,详细见枚举NSOperationQueuePriority

多线程实践

默认情况下方法是在当前线程执行。如果需要异步多线程执行,需要通过NSOperationQueue。

NSOperationQueue

    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];  //主队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //自定义队列
    [queue setMaxConcurrentOperationCount:1]//串行队列
    [queue setMaxConcurrentOperationCount:4]//count数值大于1 为并发队列
    //系统默认最大并发数-1,表示不限制。
	[queue setSuspended:YES];// 暂停队列
	BOOL res = [queue isSuspended];//判断队列是否暂停
	//暂停和取消不是立刻取消当前操作,而是等当前的操作执行完之后不再进行新的操作。
NSOperationQueue属性和方法 解释
+(id)currentQueue 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil
+(id)mainQueue 获取主队列
-(void)cancelAllOperations 可以取消队列的所有操作
-(BOOL)isSuspended 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态
-(void)setSuspended:(BOOL)b 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列
-(void)waitUntilAllOperationsAreFinished 阻塞当前线程,直到队列中的操作全部执行完毕
-(void)addOperationWithBlock:(void (^)(void))block 向队列中添加一个 NSBlockOperation 类型操作对象
-(void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
-(NSArray *)operations 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除
-(NSUInteger)operationCount 当前队列中的操作数

P.s.

  • 这里的暂停和取消(包括操作的取消和队列的取消)并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
  • 暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。
  • 你不能重用一个操作对象。一旦它被添加到一个队列中,你就丧失了对它的所有权。如果你想再使用同一个操作类,就必须创建一个新的变量。一个结束的操作不能被重启。

NSInvocationOperation

    NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(doSth) object:nil];
    NSLog(@"%@",[NSThread currentThread]);
    [operation start];

	 - (void)doSth{
	    NSLog(@"%@",[NSThread currentThread]);
	}

开启新线程:

    NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(doSth) object:nil];
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    //设置队列运行线程数
    [queue setMaxConcurrentOperationCount:4]
    [queue addOperation:operation];
    NSLog(@"%@",[NSThread currentThread]);

	- (void)doSth{
	    NSLog(@"%@",[NSThread currentThread]);
	}

NSBlockOperation

可以通过设置completionBlock监听操作的完成与否。

    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    operation.completionBlock = ^{
        // 操作完成后的操作
        NSLog(@"NSBlockOperation%@", [NSThread currentThread]);
    };
    [operation start];

单独执行一个block是在当前线程中执行,向NSBlockOperation中加入多个block,会进行异步多线程执行操作。

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

也可以将operation加入到队列中来实现多线程操作。

    NSBlockOperation *operation = [[NSBlockOperation alloc]init];
    [operation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    NSOperationQueue *queue = [NSOperationQueue new];
    //设置队列运行线程数
    [queue setMaxConcurrentOperationCount:4]
    [queue addOperation:operation];

NSOperation 操作依赖

通过操作依赖,我们可以很方便的控制操作之间的执行先后顺序。注意不能互相依赖。一个操作可以依赖其他多个操作。
可以在不同queue的NSOperation之间添加依赖。依赖高于优先级priority。

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作1");
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作2");
    }];
    [op1 addDependency:op2];
    //NSLog(@"%@",[op1 dependencies]);//获取操作的依赖集合
    //[op1 removeDependency:op2];//移除依赖
    [queue addOperation:op1];
    [queue addOperation:op2];

操作结果:
2019-11-15 11:52:13.983959+0800 iosTest[31796:1810100] 操作2
2019-11-15 11:52:13.984296+0800 iosTest[31796:1810101] 操作1

NSOperation 返回主线程

- (void)communication {
	//创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
	//添加操作
    [queue addOperationWithBlock:^{
        // 异步进行耗时操作
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); 
        }
		// 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            for (int i = 0; i < 2; i++) {
                [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
                NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
            }
        }];
    }];
}

自定义 NSOperation子类

ZJOperation.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN
@class ZJOperation;

@protocol ZJOperationDelegate <NSObject>
@optional
- (void)operation:(ZJOperation *)operation withObj:(id)obj;
@end

@interface ZJOperation : NSOperation
@property (nonatomic, weak) id<ZJOperationDelegate> delegate;
@end

NS_ASSUME_NONNULL_END

ZJOperation.m

#import "ZJOperation.h"
@implementation ZJOperation
- (void)main{
    //不能访问主线程的自动释放池,所以应该手动创建一个。
    @autoreleasepool {
    	if(self.isCancelled)return;
        //模拟耗时操作
        if ([self.delegate respondsToSelector:@selector(operation:withObj:)]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                // 返回主线程,传递数据给代理对象
                [self.delegate operation:self withObj:nil];
            });
        }
    }
}
@end

P.s.
如果需要重写start方法,需要手动管理状态有:
isExecuting 代表任务正在执行中。isFinished 代表任务已经执行完成。isCancelled 代表任务已经取消执行。
通过KVO监控NSOperation对象的属性:可以通过isCancelled属性来判断任务是否已取消,通过isFinished属性来判断任务是否已经完成等等。

发布了38 篇原创文章 · 获赞 5 · 访问量 9068

猜你喜欢

转载自blog.csdn.net/zj382561388/article/details/103065247