ios - 多线程之十一:NSOperation

NSOperation 是抽象类,所以要用子类来进行线程使用;目前是三种方式:
1:NSInvocationOperation
2:NSBlockOperation
3:创建子类继承自 NSOperation

1:NSInvocationOperation

NSInvocationOperation : 可以理解为是一个为 @selector 包装上任务特性,能够在子线程中执行;

注意事项:
1:单独使用 NSInvocationOperation 不会开启新线程,会在主线程中执行,造成主线程堵塞;
2:创建 NSInvocationOperation 对象之后需要手动开启 start ;
3:此时要结合 NSOperationQueue 组合使用;(衍生出一个问题:GCD , operation 都体现出了线程队列的概念,队列对多线程来说为什么如此重要?)
4:在和 NSOperationQueue 组合使用时,不用再通过 start 进行开启;
5:NSInvocationOperation 的使用一定要结合着 NSOperationQueue

代码展示:

NSInvocationOperation *InvocationOperationA = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationA.name = @"operation - invocationA";

NSInvocationOperation *InvocationOperationB = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationB.name = @"operation - invocationB";

NSInvocationOperation *InvocationOperationC = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationC.name = @"operation - invocationC";

NSInvocationOperation *InvocationOperationD = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationD.name = @"operation - invocationD";

NSInvocationOperation *InvocationOperationE = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run_invocationOperation) object:nil];
InvocationOperationE.name = @"operation - invocationE";


//启动任务 :NSInvocationOperation 在和 NSOperationQueue 组合使用时,不需要使用 start
//    [InvocationOperationA start];
//    [InvocationOperationB start];
//    [InvocationOperationC start];
//    [InvocationOperationD start];

//NSOperationQueue - 线程队列
NSOperationQueue *operQueue = [[NSOperationQueue alloc] init];

operQueue.name = @"oper_Queue";   //设置队列名称

[operQueue setMaxConcurrentOperationCount:2]; //设置队列中允许的最大线程并发数


//判断任务的执行状态
if (InvocationOperationA.isExecuting) {
    NSLog(@"InvocationOperationA 是执行");
}else{
    NSLog(@"InvocationOperationA 还未执行");
}


/*
 NSOperationQueue - 两种在队列中添加事务的方式;
    1 : addOperation
    2 : addOperationWithBlock
 */
//在队列中添加要执行的操作或者任务;
[operQueue addOperation:InvocationOperationA];
[operQueue addOperation:InvocationOperationB];
[operQueue addOperation:InvocationOperationC];
[operQueue addOperation:InvocationOperationD];
[operQueue addOperation:InvocationOperationE];

2:NSBlockOperation

NSBlockOperation 两种方式存放任务代码

1:类方法 - blockOperationWithBlock
** 2:实例方法 - addExecutionBlock**

注意事项:
1:单独使用 NSBlockOperation 设置一个 addExecutionBlock 不会开启新线程,会在主线程中执行,造成主线程堵塞;
2:单独使用 NSBlockOperation 中的 addExecutionBlock 方式设置任务时候,任务除在主线程执行之外,还会开启新的子线程来执行其他任务。但还是会造成主线程的堵塞;
3:创建 NSBlockOperation 对象之后需要手动开启 start ;
4:此时要结合 NSOperationQueue 组合使用;(衍生出一个问题:GCD , operation 都体现出了线程队列的概念,队列对多线程来说为什么如此重要?)
5:在和 NSOperationQueue 组合使用时,不用再通过 start 进行开启;

实现组合两种: 都不会主线程的堵塞

第一种 :
1 :NSBlockOperation 实例化一个对象;
2 :使用 addExecutionBlock 添加要执行的任务代码;可以添加多个;
3 :创建 NSOperationQueue 实例
4 :在 NSOperationQueue 实例中添加 NSBlockOperation 实例
5 :不要再使用 start 进行启动了

优缺点:
(优点)1:不用创建很多 NSBlockOperation 对象,在一个对象添加多个任务的 block 代码;
(优点)2:会根据 NSBlockOperation 中添加的 block 块数量开启相应多的子线程执行任务;(需要思考的问题:为什么会根据 block 开启子线程?是不是和 block 的实现原理有关,或者和其特殊属性有关?)
(缺点)3:通过 NSOperationQueue 的函数 setMaxConcurrentOperationCount 控制并发数是不生效的;

第二种 :
1 :NSBlockOperation 实例化多个对象;
2 :在每个 NSBlockOperation 实例对象中设置任务的 block 代码;
3 :创建 NSOperationQueue 实例
4 :在 NSOperationQueue 实例中添加所有要使用的 NSBlockOperation 实例
5 :不要再使用 start 进行启动了

优缺点:
(优点)1:可以通过 NSOperationQueue 的函数 setMaxConcurrentOperationCount 控制并发数;
(优点)2:会根据 NSBlockOperation 的 setMaxConcurrentOperationCount 设置数量开启相应多的子线程执行任务;
(缺点)3:需要为每一个任务的 block 块创建一个 NSBlockOperation 实例;

代码展示:

[NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"类方法 blockOperation - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
    }
}];


NSBlockOperation *blockOperation = [[NSBlockOperation alloc] init];
blockOperation.name = @"blockOperation";
[blockOperation addExecutionBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"blockOperation - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
    }
}];
[blockOperation addExecutionBlock:^{
    for (int i = 20; i <= 30 ; i ++) {
        sleep(1);
        NSLog(@"blockOperation - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
    }
}];
[blockOperation addExecutionBlock:^{
    for (int i = 30; i <= 40 ; i ++) {
        sleep(1);
        NSLog(@"blockOperation - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
    }
}];

//    [blockOperation start];




NSBlockOperation *blockOperationA = [[NSBlockOperation alloc] init];
blockOperationA.name = @"blockOperationA";
[blockOperationA addExecutionBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"blockOperationA - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
    }
}];
NSBlockOperation *blockOperationB = [[NSBlockOperation alloc] init];
blockOperationB.name = @"blockOperationB";
[blockOperationB addExecutionBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"blockOperationB - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
    }
}];
NSBlockOperation *blockOperationC = [[NSBlockOperation alloc] init];
blockOperationC.name = @"blockOperationC";
[blockOperationC addExecutionBlock:^{
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"blockOperationC - 当前线程名称:%@ ——%d",[NSThread currentThread].name,i);
    }
}];

NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:2];
//方式一
[operationQueue addOperation:blockOperation];



//方式二
//    [operationQueue addOperation:blockOperationA];
//    [operationQueue addOperation:blockOperationB];
//    [operationQueue addOperation:blockOperationC];

3:创建子类继承自 NSOperation

//NSOperation - 自定义线程 - CustomOperation
*
个人解读:
所谓的自定义线程就是在继承 NSOperation 之后,在类内部设置好该类要执行的特定任务,也就是说自定线程,自定义的部分就是明确该线程要执行的操作是什么。
*

1:创建文件

自定义线程

2:在.m文件重写 main 方法

  - (void) main {

//NSOperation - 自定义使用实现子线程操作 - 同步任务

NSLog(@"线程名字 = %@",self.name);
for (int i = 0; i <= 10 ; i ++) {
    sleep(1);
    NSLog(@"自定义线程 %@——%d",self.name,i);
}
}

3:如果在自定义 NSOperation 时 自定义使用实现子线程操作 - 异步任务,

NSLog(@"开始任务");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    if (self.isCancelled) {
        return ;
    }
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"自定义线程 ——%d",i);
    }
    NSLog(@"循环结束");
    self.over = YES;
});
NSLog(@"结束任务");

执行结果:

自定义线程,异步任务

该如何解决:

NSLog(@"开始任务");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    if (self.isCancelled) {
        return ;
    }
    for (int i = 0; i <= 10 ; i ++) {
        sleep(1);
        NSLog(@"自定义线程 ——%d",i);
    }
    NSLog(@"循环结束");
    self.over = YES;
});

//解决办法 : 使用 NSRunLoop 进行循环执行
/*
    通过 NSRunLoop 的控制线程,等待异步任务完成之后再让该线程执行完成。
 */
while (!self.over && !self.isCancelled) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}


NSLog(@"结束任务");

NSOperation有一个很大的便捷就是处理线程依赖:

CustomOperation *operation1 = [[CustomOperation alloc] initWithName:@"自定义线程一"];
CustomOperation *operation2 = [[CustomOperation alloc] initWithName:@"自定义线程二"];
CustomOperation *operation3 = [[CustomOperation alloc] initWithName:@"自定义线程三"];
CustomOperation *operation4 = [[CustomOperation alloc] initWithName:@"自定义线程四"];
CustomOperation *operation5 = [[CustomOperation alloc] initWithName:@"自定义线程五"];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"自定义线程的所在队列";
[queue setMaxConcurrentOperationCount:2];



//NSOperation - 线程依赖 Dependency(依赖)
/*
 添加上依赖之后,两个线程就有了依赖关系,举例说明, A 设置了依赖 B ,带代码执行阶段时, 只有当 B 执行完之后才会执行 A;
 
 应用场景:
 在实际开发过程中,任务3 之后再任务1和任务2完成之后才能执行就可以使用依赖关系
 
 注意事项:
 再设置依赖关系的时候要注意不要添加依赖关系形成闭环,出现互相依赖的情况,会导致程序不执行;
 */
[operation1 addDependency:operation5];
[operation5 addDependency:operation2];
[operation2 addDependency:operation4];
[operation4 addDependency:operation3];
//    [operation3 addDependency:operation1];  这一点一定要注意,千万要捋顺彼此的依赖关系

//一对多的依赖关系
/*
    只有当多个有依赖的任务执行完成之后才会执行
 */
//    [operation1 addDependency:operation5];
//    [operation1 addDependency:operation2];
//    [operation2 addDependency:operation4];
//    [operation4 addDependency:operation3];


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

推荐阅读更多精彩内容