浅读NSOperationQueue&NSOperation

NSOperationQueue

  NSOperationQueue是管理一组操作(operation)的队列。队列中操作是根据操作的优先级(queuePriority)和操作的依赖关系(Dependency)来决定执行的先后顺序。操作一旦加入到队列中,该操作会一直存在这个队列中,不能直接从队列中删除,除非改操作被显示的取消( 调用cancel() )或者完成才能移除。
  注意:取消操作会导致操作忽略它可能拥有的任何依赖关系,例如:operationA依赖operationB,并且operationB还没执行,此时你想取消operationA,这个时候operationA不会等operationB完成后在执行取消操作,而是直接执行start方法将operationA标记为完成状态,然后从队列中删除。
  队列通常会提供运行操作的线程,使用libdispatch库(中央调度)启动它们的操作。操作不管是异步或者同步,都是在单独的线程上执行,所以可以安全的使用多个线程中的单个NSOperationQueue对象,不需要创建额外的锁来同步该对象的访问。
让我们看看NSOperationQueue的属性和方法:

//操作数组,只读
@property (readonly, copy) NSArray<__kindof NSOperation *> *operations; 
//包含操作的数量,只读
@property (readonly) NSUInteger operationCount ;  
//队列中同时可执行的最大操作数量
@property NSInteger maxConcurrentOperationCount;
//是否暂停执行操作 
@property (getter=isSuspended) BOOL suspended;  
// 队列的名字
@property (nullable, copy) NSString *name ; 
//系统管理的队列的资源分配 
@property NSQualityOfService qualityOfService;
//依赖的现有队列,默认为nil
@property (nullable, assign /* actually retain */) dispatch_queue_t underlyingQueue;
//当前的操作队列
@property (class, readonly, strong, nullable) NSOperationQueue *currentQueue
//当前主线程关联的队列
@property (class, readonly, strong) NSOperationQueue *mainQueue
//添加操作对象
- (void)addOperation:(NSOperation *)op;
//添加操作对象组    waitUntilFinished:是否阻塞当前线程,等所有操作都完成
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
//添加操作任务
- (void)addOperationWithBlock:(void (^)(void))block;
//取消所有的操作
- (void)cancelAllOperations;
//阻塞线程知道所有操作完成
- (void)waitUntilAllOperationsAreFinished;

代码实现一下:

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.name = @"OperationQueueTest";
    NSLog(@"%@",queue.name);
    queue.maxConcurrentOperationCount = 2;
    NSOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operationA:%@", [NSThread currentThread]);
        //为了检验maxConcurrentOperationCount属性
        sleep(5);
    }];
    NSOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operationB:%@", [NSThread currentThread]);
        sleep(5);
    }];
    NSOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operationC:%@", [NSThread currentThread]);
    }];
    NSOperation *operationD = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operationD:%@", [NSThread currentThread]);
    }];
    NSOperation *operationE = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"operationE:%@", [NSThread currentThread]);
    }];
   //operationD依赖于A,B,C,所以必须等ABC完成后才能执行D
    [operationD addDependency:operationA];
    [operationD addDependency:operationB];
    [operationD addDependency:operationC];
  
    [queue addOperation:operationA];
    [queue addOperation:operationB];
    [queue addOperation:operationC];
    [queue addOperation:operationD];
    [queue addOperation:operationE];

从下面的输出可以看到,运行后可以看到线程的最大并发数为2,D在ABC完成后才执行。


operationQueue.png

不过这里有个小问题,为什么线程都是从number = 3开始的,搜了一些资料,也尝试了一下,只知道number = 1 为主线程,那么number = 2 是什么?如果有读者知道希望告诉我一下,不胜感激!

NSOperation

  NSOperation是表示与单个任务关联的代码和数据的抽象类,所以不能直接使用,需要使用系统定义的子类(NSInvocationOperation和NSBlockOperation)来执行实际的任务。虽然NSOperation是抽象的,但是它的基本实现包含了重要的逻辑来协调任务的安全执行。
  如果你不想使用NSOperationQueue,可以直接调用NSOperation的start() 方法来执行操作,只能二选一。手动执行操作会增加代码的负担,因为启动不处于就绪状态的操作会触发异常。

//开始操作
- (void)start;
//操作任务的入口,一般用于自定义的NSOperation的子类
- (void)main;
//操作是否取消,默认为NO
@property (readonly, getter=isCancelled) BOOL cancelled;
//取消操作
- (void)cancel;
//操作是否执行
@property (readonly, getter=isExecuting) BOOL executing;
//操作是否完成
@property (readonly, getter=isFinished) BOOL finished;
//操作是否异步执行其他任务
@property (readonly, getter=isConcurrent) BOOL concurrent; 
//操作是否异步执行任务
@property (readonly, getter=isAsynchronous) BOOL asynchronous API_AVAILABLE(macos(10.8), ios(7.0), watchos(2.0), tvos(9.0));
//操作是否准备被执行
@property (readonly, getter=isReady) BOOL ready;
//添加依赖
- (void)addDependency:(NSOperation *)op;
//移除依赖
- (void)removeDependency:(NSOperation *)op;
//操作依赖的的操作组
@property (readonly, copy) NSArray<NSOperation *> *dependencies;
//操作在队列的优先级
@property NSOperationQueuePriority queuePriority;
//操作的主要任务发出后的回调
@property (nullable, copy) void (^completionBlock)(void) API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
//操作完成前是否阻塞当前线程的执行
- (void)waitUntilFinished API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
//操作的线程优先级(弃用)
@property double threadPriority API_DEPRECATED("Not supported", macos(10.6,10.10), ios(4.0,8.0), watchos(2.0,2.0), tvos(9.0,9.0));
//系统资源的分配
@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
//操作名字
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

NSBlockOperation

  NSBlockOperation是NSOperation的子类,用来管理一个或者多个block的并发执行的操作。一个blockOperation可以执行一个或多个block,当执行多个block的时候,要等所有block都执行完成后,才考虑operation本身。

//创建一个带有block的opertation对象。
+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
//添加执行的block
- (void)addExecutionBlock:(void (^)(void))block;
//opertaion中含有的所有执行block
@property (readonly, copy) NSArray<void (^)(void)> *executionBlocks;
// 创建
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{}];
// 操作完成的时候回调
[operationA setCompletionBlock:^{}];
// 添加任务
[operation addExecutionBlock:^{}];
// 开始 异步并发:第一个任务在当前线程,其他任务在其他线程
[operation start];
// 添加到队列 所有任务都在同一个队列中
[queue addOperation:operation1];
// 设置依赖   A->B->C
[operationB addDependency:operationA];
[operationC addDependency:operationB];

NSInvocationOperation

  NSInvocationOperation是NSOperation的子类,管理那些指定为调用的封装好的单个任务的执行。用来发起一个操作,这个操作能调用指定对象上的方法。

//创建一个调用特定对象的方法的invocationOperation
- (nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
//根据一个NSInvocation对象 创建一个InvocationOpetation
- (instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;
// 包含的invocation对象
@property (readonly, retain) NSInvocation *invocation;
//调用方法或者invocation的结果
@property (nullable, readonly, retain) id result;
// 创建
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
// 开始 同步执行(在当前线程执行操作)
[operation start];
// 添加操作到队列中,会自动异步执行
[queue addOperation:operation];
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 本文首发于我的个人博客:「程序员充电站」[https://itcharge.cn]文章链接:「传送门」[https...
    ITCharge阅读 109,637评论 134 749
  • 概述 这篇文章中,我不会说多线程是什么、线程和进程的区别、多线程有什么用,当然我也不会说什么是串行、什么是并行等问...
    hashakey阅读 292评论 0 0
  • 一、简介 除了,NSThread和GCD实现多线程,配合使用NSOperation和NSOperationQueu...
    怎样m阅读 960评论 0 5
  • 目录 一、基本概念1.多线程2.串行和并行, 并发3.队列与任务4.同步与异步5.线程状态6.多线程方案 二、GC...
    BohrIsLay阅读 1,557评论 5 12
  • 应怜碧波待人开 画眉啼窗久难猜 湖畔青柳吹又续 翘首向东为谁发
    如丹阅读 88评论 0 0