NSOperation简单使用

配合使用NSOperation 和 NSOperationQueue 实现多线程编程。

NSOperation实现多线程的步骤

1, 将要执行的操作封装到NSOperation对象中
2, 将NSOperation 对象添加到 NSOperationQueue中
3, 系统自动将NSOperationQueue中的NSOperation 取出来
4, 将NSOperation 封装的操作放到一条新线程执行

NSOperation的子类

NSOperation是个抽象类,并不具有封装操作的能力。使用方式有3种
1,NSInvocationOperation
2,NSBlockOperation
3,自定义子类继承NSOperation ,实现内部相应的方法

NSOperationQueue 队列组

1 ,默认就是并发执行的啦
2, 那么我们怎么设置串行执行呢 。队列组有个属性 maxConcurrentOperationCount 设置最大并发数 ,就可以啦,这样就是串行执行的了
maxConcurrentOperationCount = 1 ;串行队列
maxConcurrentOperationCount > 1 ; 并发队列
maxConcurrentOperationCount = 0 ; 不会执行任务
maxConcurrentOperationCount = -1 ; 特殊意义。表示的是最大值。
默认并发数就是 static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = -1;

/*
  暂停可恢复,取消不可恢复
  队列中的任务也是有状态的:已经执行完毕 | 正在执行|排队等待 ,不管是暂停还是取消都会等待正在执行的任务结束才会执行。
*/

//暂停  ,不能暂停当前正在执行的任务
    [queue setSuspended:YES];
// 继续
     [queue setSuspended:NO];
/*取消,不可恢复 。取消队列里面的所有任务,我们能拿到 cancelled 属性,
这样就可以判断队列是否取消任务,可以在 队列任务里面进行判断,
用于即时取消任务等,因为正在执行的任务不会里面执行,
所有cancelled 。可以帮助判断*/
    [queue cancelAllOperations];

NSOperationQueue 的一个只读属性 cancelled 。返回BOOL,队列是否取消了任务

3,注意:串行执行 != 只开了一条线程

方式一 :NSInvocationOperation 简单的封装任务,封装操作,不和NSOperationQueue配合使用的时候,没有任何作用,并不会开启子线程。


-(void)invocationOperation{
    //1,封装操作,封装任务
    /*
     1.参数1:目标对象 self
     2.参数2:调用的方法
     3.参数3:方法的参数
     */
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil];

    /*
     [NSOperationQueue mainQueue]; 获取主队列
     NSOperation队列:
     1,主队列 : [NSOperationQueue mainQueue] 和gcd主队列一样,任务都在主线程执行,主队列是串行队列
     2,非主队列:[[NSOperationQueue alloc] init] 特殊(同时具备并发和串行的功能),默认情况下非主队列是并发队列
     */
    
    //2 ,创建队列
   NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //设置最大并发数的数量,同一时间最多多少任务可以执行 ,设置为1 就是串行啦
    queue.maxConcurrentOperationCount = 2;
    //3 , 添加操作到队列中 ,
    [queue addOperation:op1]; //内部已经调用了  [op1 start]; ,调用了start后又调用了内部的main方法 。我们可以用继承重写start,和main 方法。把任务封装到 main方法里面
    [queue addOperation:op2];
    [queue addOperation:op3];
    
    //,启动操作 ,如果不 和NSOperationQueue 配合操作。就必须手动启动一下,
    //并且NSInvocationOperation,单独使用并不会开启多线程
    //[op1 start];
}

-(void)test
{
    NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
}
-(void)test1
{
    NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
}
-(void)test2
{
    NSLog(@"%s __func__  NSThread  = %@" ,__func__,[NSThread currentThread]);
}
/* 打印 ,看打印,不难理解,内部是并发执行
2017-11-06 23:08:11.404 NSOperationDemo[1847:56183] -[ViewController test] __func__  NSThread  = <NSThread: 0x6080000776c0>{number = 4, name = (null)}
2017-11-06 23:08:11.404 NSOperationDemo[1847:56165] -[ViewController test2] __func__  NSThread  = <NSThread: 0x60800007b140>{number = 5, name = (null)}
2017-11-06 23:08:11.404 NSOperationDemo[1847:56166] -[ViewController test1] __func__  NSThread  = <NSThread: 0x60000007b9c0>{number = 3, name = (null)}
*/

方式二 :NSBlockOperation 单独使用不配合 queue 队列, 不会开启子线程,但是一个操作中任务数量大于1 ,会开启子线程,详情如下

-(void)blockOperation
{
    //1,创建任务
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3 -- %@",[NSThread currentThread]);
    }];
    
    
    //2 ,追加任务
    //注意: 如果一个操作中任务数量大于1 , 会开子线程并发执行任务,
    并不一定是子线程也许是主线程,比如op3 就有4个任务,所以会开子线程,
    并发执行任务。而 op1 ,op2,不会开子线程在主线程执行
    [op3 addExecutionBlock:^{
        NSLog(@"4 -- %@",[NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"5 -- %@",[NSThread currentThread]);
    }];
   
    [op3 addExecutionBlock:^{
        NSLog(@"6 -- %@",[NSThread currentThread]);
    }];
    
    //3 ,启动
    [op1 start];
    [op2 start];
    [op3 start];
  
}

/* 打印结果
2017-11-06 22:38:20.505 NSOperationDemo[1399:33218] 1 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
2017-11-06 22:38:20.505 NSOperationDemo[1399:33218] 2 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33218] 3 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33218] 5 -- <NSThread: 0x60800006ba80>{number = 1, name = main}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33259] 4 -- <NSThread: 0x600000076740>{number = 3, name = (null)}
2017-11-06 22:38:20.506 NSOperationDemo[1399:33261] 6 -- <NSThread: 0x6000000766c0>{number = 4, name = (null)}
*/

方式二:NSBlockOperation 单独使用配合 queue 队列,开启多线程

#pragma mark NSBlockOperation 配合队列   queue 的 使用
-(void)blockOperationWithQueue
{
    //1,创建任务
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3 -- %@",[NSThread currentThread]);
    }];
    
    //2 ,追加任务
    //注意: 如果一个操作中任务数量大于1 , 会开子线程并发执行任务,并不一定是子线程也许是主线程,比如op3 就有4个任务,所以会开子线程,并发执行任务
    [op3 addExecutionBlock:^{
        NSLog(@"4 -- %@",[NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"5 -- %@",[NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"6 -- %@",[NSThread currentThread]);
    }];
    
    //2 ,创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //3 ,启动
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    
    //简便方法 ,相当于,创建了任务,并添加到了 queue
    [queue addOperationWithBlock:^{
        NSLog(@"7 -- %@",[NSThread currentThread]);
    }];
}

/* 打印如下
  2017-11-06 23:17:08.596 NSOperationDemo[1886:61154] 7 -- <NSThread: 0x600000263940>{number = 7, name = (null)}
2017-11-06 23:17:08.596 NSOperationDemo[1886:61099] 1 -- <NSThread: 0x60000007f140>{number = 6, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61155] 2 -- <NSThread: 0x6000002603c0>{number = 8, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61154] 3 -- <NSThread: 0x600000263940>{number = 7, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61113] 4 -- <NSThread: 0x60800007be40>{number = 4, name = (null)}
2017-11-06 23:17:08.598 NSOperationDemo[1886:61099] 5 -- <NSThread: 0x60000007f140>{number = 6, name = (null)}
2017-11-06 23:17:08.599 NSOperationDemo[1886:61155] 6 -- <NSThread: 0x6000002603c0>{number = 8, name = (null)
*/

方式三:自定义子类继承NSOperation ,实现内部相应的方法

好处:1,利于代码隐蔽
2, 复用性,耦合性

#import <Foundation/Foundation.h>
//XCOperation 继承子类实现
@interface XCOperation : NSOperation
@end

#import "XCOperation.h"
@implementation XCOperation
//告诉要执行的任务是什么
-(void)main{
    NSLog(@"__Func__ %s , NSThread = %@",__func__,[NSThread currentThread]);
}

#pragma mark 继承 NSOperation  使用方法
-(void)customOperation
{
    //1 ,封装操作
    XCOperation *op1 = [[XCOperation alloc] init];
    XCOperation *op2 = [[XCOperation alloc] init];

    //2 ,创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //3 ,添加 启动
    [queue addOperation:op1];
    [queue addOperation:op2];
}
/* 打印
 NSOperationDemo[1950:69064] __Func__ -[XCOperation main] , NSThread = <NSThread: 0x600000074880>{number = 3, name = (null)}
 NSOperationDemo[1950:69044] __Func__ -[XCOperation main] , NSThread = <NSThread: 0x600000075680>{number = 4, name = (null)}
*/

NSBlockOperation 操作依赖,控制任务顺序 ,监听任务完成情况。

#pragma mark NSBlockOperation 操作依赖 ,控制任务顺序 ,于操作监听,监听某个任务完成状态
-(void)blockOperationWithQueueRelyon
{
    //1,创建任务
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2 -- %@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 8; i ++) {
            NSLog(@"3 -- %@",[NSThread currentThread]);
            if (i == 7) {
                NSLog(@"任务3 ,打印到了7 跳出循环任务结束");
                break;
            }
        }
    }];
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"4 -- %@",[NSThread currentThread]);
    }];
    
    //2 ,创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];

    //3.操作依赖
    //注意:不能循环依赖,比如操作1 依赖 操作3 。操作3 又来依赖操作1 。会互相等待
    //这里是操作1依赖操作3 。操作3 依赖操作2. 所有打印顺序是  2 -> 3 -> 1 -> 4
    //可以跨队列依赖
    [op1 addDependency:op3]; // 任务1 依赖任务3
    [op3 addDependency:op2]; // 任务3 依赖任务2
    [op4 addDependency:op1]; // 任务4 依赖任务1  所以 打印顺序是  2 -> 3 -> 1 -> 4
    
    //4.添加 到队列组
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue1 addOperation:op4];
    
    //5,操作监听,监听任务3 ,任务3 完成了,打印这里
    op3.completionBlock = ^{
        NSLog(@"监听到任务3结束了 ,虽然监听到任务3结束,但是他和任务3并不一定在一个线程 ,%@",[NSThread currentThread]);
    };
    
}
/* 可以看出顺序是2 3 1 4 ,监听到了任务3
2017-11-07 22:52:18.344 NSOperationDemo[1045:29786] 2 -- <NSThread: 0x60800007b7c0>{number = 7, name = (null)}
2017-11-07 22:52:18.346 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.346 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.347 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.347 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.348 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.348 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 3 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.350 NSOperationDemo[1045:30254] 任务3 ,打印到了90 跳出循环任务结束
2017-11-07 22:52:18.351 NSOperationDemo[1045:29786] 监听到任务3结束了 ,虽然监听到任务3结束,但是他和任务3并不一定在一个线程 ,<NSThread: 0x60800007b7c0>{number = 7, name = (null)}
2017-11-07 22:52:18.351 NSOperationDemo[1045:30254] 1 -- <NSThread: 0x608000077700>{number = 10, name = (null)}
2017-11-07 22:52:18.354 NSOperationDemo[1045:29786] 4 -- <NSThread: 0x60800007b7c0>{number = 7, name = (null)}

*/

线程之间的通信 。例如: 子线程下载图片,下载完成通知主线程刷新

#pragma makr 线程中的通信 ,例子子线程下载图片,主线程刷新

-(void)downloadImage
{
    //1 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    //2 封装任务
    NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{
        NSURL *url = [NSURL URLWithString:@"http://static.firefoxchina.cn/img/201710/4_59e999c8e6aa50.jpg"];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        NSLog(@"子线程下载图片%@" ,[NSThread currentThread]);
        UIImage *image = [UIImage imageWithData:imageData];
      
        //4,线程通信,回到主线程,刷新UI
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageview.image = image ;
        }];

//      [queue addOperationWithBlock:^{
//           NSLog(@"7 -- %@",[NSThread currentThread]);
//      }];
//        [queue addOperationWithBlock:^{
//            NSLog(@"8 -- %@",[NSThread currentThread]);
//        }];
//        [queue addOperationWithBlock:^{
//            NSLog(@"9 -- %@",[NSThread currentThread]);
//        }];

    }];
    
    //3 添加到queue
    [queue addOperation:download];
}

学习记录,简单的敲一下增强记忆,便于查找,😄

NSThread简单的入门
GCD入门

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

推荐阅读更多精彩内容