iOS多线程之NSOperationQueue-依赖、并发数、优先级、自定义Operation等最全的使用总结

简介: iOS多线程之NSOperationQueue-依赖、并发数、优先级、自定义Operation等最全的使用总结

1. 简介

NSOperation(任务): 通过start方法开始执行任务、默认是同步执行的

NSOperationQueue(队列): 将NSOperation添加到队列中执行(主队列除外),是异步执行的


2. NSOperation创建

1) NSOperation一般不直接使用,而是使用它的子类NSInvocationOperation和NSBlockOperation

2) 可以看到在没有添加到对列的时候都是在当前线程执行的

文末附工程源码



- (void)createOperation {
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    [op start];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    [blockOp start];
}
- (void)createOperation {
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    [op start];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    [blockOp start];
}

运行结果:



2021-04-11 11:12:24.522251+0800 OperationQueue[1485:43624] obj - <NSThread: 0x600003a0c900>{number = 1, name = main}
2021-04-11 11:12:24.522431+0800 OperationQueue[1485:43624] block - <NSThread: 0x600003a0c900>{number = 1, name = main}


3. 将NSOperation添加到Queue中

1)可以利用[queue addOperation:op]将NSOperation添加到NSOperationQueue中

2)也可以利用[queue addOperations:@[op, blockOp] waitUntilFinished:NO]将NSOperation添加到NSOperationQueue中

3注意一不可用重复添加同一个NSOperation到NSOperationQueue中,否则会报如下错误

- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
//    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
//    [op start];
//    [op cancel];
}
- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
//    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
//    [op start];
//    [op cancel];
}

运行结果:



*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperations:waitUntilFinished:]: 2 (of 2) operations are finished, executing, or already in a queue, and cannot be enqueued'
terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperations:waitUntilFinished:]: 2 (of 2) operations are finished, executing, or already in a queue, and cannot be enqueued'
terminating with uncaught exception of type NSException

4注意二不可将NSOperation调用start或者cancel之后再添加到NSOperationQueue中,否则会报如下错误

- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
//    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
//    [op start];
//    [op cancel];
}

运行结果:



*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued'
terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued'
terminating with uncaught exception of type NSException

5注意三不可将NSOperation添加NSOperationQueue之后,再次调用star或者cancelt去修改状态,否则会报如下错误

- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
//    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
//    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
    [op start];
//    [op cancel];
}
- (void)addToQueue {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
//    [op start];//调用start之后就不可以再入队
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    //添加单个
    [queue addOperation:op];
    [queue addOperation:blockOp];
    //添加一组
//    [queue addOperations:@[op, blockOp] waitUntilFinished:NO];
    [op start];
//    [op cancel];
}

运行结果:



*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSInvocationOperation start]: something is trying to start the receiver simultaneously from more than one thread'
terminating with uncaught exception of type NSException


4. 添加依赖关系

1)当需要某个NSOperation对象依赖于其它NSOperation对象时,可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作的时候,当前NSOperation对象才会开始执行操作,也可以通过removeDependency方法来删除依赖对象

2)不添加依赖关系应该先打印block再打印obj见上方日志,设置依赖后见下方代码说明

3)设置依赖关系,必须在addOperation之前设置,否则没有效果

4)可以相互依赖,否则都不会执行


- (void)addDependency {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    [op addDependency:blockOp];//添加依赖
//    [blockOp addDependency:op];//相互依赖
//    [op removeDependency:blockOp];//移除依赖
    [queue addOperation:op];
    [queue addOperation:blockOp];
}
- (void)addDependency {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
    }];
    [op addDependency:blockOp];//添加依赖
//    [blockOp addDependency:op];//相互依赖
//    [op removeDependency:blockOp];//移除依赖
    [queue addOperation:op];
    [queue addOperation:blockOp];
}

运行结果:



2021-04-11 11:29:47.303916+0800 OperationQueue[1586:56394] block - <NSThread: 0x600003ead180>{number = 5, name = (null)}
2021-04-11 11:29:47.304047+0800 OperationQueue[1586:56394] obj - <NSThread: 0x600003ead180>{number = 5, name = (null)}
2021-04-11 11:29:47.303916+0800 OperationQueue[1586:56394] block - <NSThread: 0x600003ead180>{number = 5, name = (null)}
2021-04-11 11:29:47.304047+0800 OperationQueue[1586:56394] obj - <NSThread: 0x600003ead180>{number = 5, name = (null)}


5. 优先级

1)执行顺序规则

不设置依赖关系的前提下:(可通过调试下方代码看执行结果)

(1)如果都不设置优先级、并且内部没有耗时操作,则按照添加顺序执行;

(2)如果都不设置优先级、并且内部有耗时操作,则耗时操作的后执行,没有耗时操作的先执行;

(3)如果设置了优先级、并且内部没有耗时操作,则按照处于ready状态的operation优先级等级的高低执行;

(4)如果设置了优先级、并且内部有耗时操作,则耗时操作的后执行,没有耗时操作的按照处于ready状态的operation优先级等级的高低执行;

2)优先级不能替代依赖关系并且依赖关系高于优先级的设置,在保证依赖关系的情况下才能考虑优先级


- (void)setQueuePriority {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
        sleep(3);
    }];
    [op setQueuePriority:NSOperationQueuePriorityHigh];
    [blockOp setQueuePriority:NSOperationQueuePriorityLow];
    [op addDependency:blockOp];
    [queue addOperation:op];
    [queue addOperation:blockOp];
}
- (void)setQueuePriority {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationSel:) object:@"obj"];
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"block - %@", [NSThread currentThread]);
        sleep(3);
    }];
    [op setQueuePriority:NSOperationQueuePriorityHigh];
    [blockOp setQueuePriority:NSOperationQueuePriorityLow];
    [op addDependency:blockOp];
    [queue addOperation:op];
    [queue addOperation:blockOp];
}

运行结果:


2021-04-11 11:39:07.960943+0800 OperationQueue[1621:62599] block - <NSThread: 0x600000715640>{number = 3, name = (null)}
2021-04-11 11:39:10.964436+0800 OperationQueue[1621:62599] obj - <NSThread: 0x600000715640>{number = 3, name = (null)}
2021-04-11 11:39:07.960943+0800 OperationQueue[1621:62599] block - <NSThread: 0x600000715640>{number = 3, name = (null)}
2021-04-11 11:39:10.964436+0800 OperationQueue[1621:62599] obj - <NSThread: 0x600000715640>{number = 3, name = (null)}

6. NSOperationQueue的挂起和恢复

可以根据你实际的需求来确定是否要挂起和恢复,挂起或者恢复NSOperationQueue,并不会影响正在执行的NSOperation

    [queue setSuspended:YES];//挂起
    [queue setSuspended:NO];//恢复
    [queue setSuspended:YES];//挂起
    [queue setSuspended:NO];//恢复


7. 等待完成

1) [blockOp waitUntilFinished]等待当前的操作完成后,才会继续向下执行,注意:会阻塞线程

2) [queue waitUntilAllOperationsAreFinished]等待所有的操作完成后,才会继续向下执行,注意:会阻塞线程



[blockOp waitUntilFinished];
[queue waitUntilAllOperationsAreFinished];
[blockOp waitUntilFinished];
[queue waitUntilAllOperationsAreFinished];


8. 取消Operations

取消所有正在执行的操作


[queue cancelAllOperations];// 两个操作都不会执行

9. 并发数

设置单次最大的执行数量,如果多于1个,是完成一个向队列里面追加一个的执行,而不是等待同时执行的最大个数执行完之后再执行后面的操作


queue.maxConcurrentOperationCount = 1;

10. 自定义NSOperation

1)NSInvocationOperation和NSBlockOperation不能满足我们的需求时就需要我们自定义,自定义NSOperation的场景主要是为了我们能更好的控制一个任务,什么情况下开始、取消、完成, 并且可以分离代码的实现

2)实现步骤:(可参考下面代码)

(1实现start方法

(2)重写isAsynchronous,是否为并发任务、根据自己的情况即可

(3)main方法可根据自己的实际需要决定是否实现、一般实现并在里面做自己的事

(4)finished、executing更新任务的状态,用于触发KVO

(5)自己的任务完成后,一定要把当前的任务状态改为完成,否则队列中后面的任务,将永远不会执行



@property(assign, nonatomic, getter=isFinished) BOOL finished;
@property(assign, nonatomic, getter=isExecuting) BOOL executing;
@property(assign, nonatomic, getter=isStart) BOOL start;
@property(assign, nonatomic, getter=isFinished) BOOL finished;
@property(assign, nonatomic, getter=isExecuting) BOOL executing;
@property(assign, nonatomic, getter=isStart) BOOL start;

@implementation CustomOperation
@synthesize finished = _finished, executing = _executing;
- (BOOL)isAsynchronous {
    return NO;
}
- (void)setFinished:(BOOL)finished {
    [self willChangeValueForKey:@"finished"];
    _finished = finished;
    [self didChangeValueForKey:@"finished"];
}
- (void)setExecuting:(BOOL)executing {
    [self willChangeValueForKey:@"executing"];
    _executing = executing;
    [self didChangeValueForKey:@"executing"];
}
- (void)start {
    @synchronized (self) {
        self.start = YES;
        if (self.isCancelled) {
            [self completed];
            return;
        }
        @autoreleasepool {
            [self main];
        }
        self.executing = YES;
    }
}
- (void)main {//在此处做一些更复杂的操作
    NSLog(@"准备开始执行任务啦~ %@", [NSThread currentThread]);
    //模拟3s后执行doSomething
    [self performSelector:@selector(doSomething:) withObject:nil afterDelay:3.0];
    [[NSRunLoop currentRunLoop] run];//此处需要添加到Runloop,子线程默认没有开启,后期文章会具体说明
}
- (void)doSomething:(id)obj {
    NSLog(@"完成了当前的任务!%@", [NSThread currentThread]);
    [self completed];
}
- (void)completed {
    @synchronized (self) {
        self.executing = NO;
        self.finished = YES;
    }
}
- (void)cancel {
    [super cancel];
    if (self.isStart) {
        [self completed];
    }
}
@end
@implementation CustomOperation
@synthesize finished = _finished, executing = _executing;
- (BOOL)isAsynchronous {
    return NO;
}
- (void)setFinished:(BOOL)finished {
    [self willChangeValueForKey:@"finished"];
    _finished = finished;
    [self didChangeValueForKey:@"finished"];
}
- (void)setExecuting:(BOOL)executing {
    [self willChangeValueForKey:@"executing"];
    _executing = executing;
    [self didChangeValueForKey:@"executing"];
}
- (void)start {
    @synchronized (self) {
        self.start = YES;
        if (self.isCancelled) {
            [self completed];
            return;
        }
        @autoreleasepool {
            [self main];
        }
        self.executing = YES;
    }
}
- (void)main {//在此处做一些更复杂的操作
    NSLog(@"准备开始执行任务啦~ %@", [NSThread currentThread]);
    //模拟3s后执行doSomething
    [self performSelector:@selector(doSomething:) withObject:nil afterDelay:3.0];
    [[NSRunLoop currentRunLoop] run];//此处需要添加到Runloop,子线程默认没有开启,后期文章会具体说明
}
- (void)doSomething:(id)obj {
    NSLog(@"完成了当前的任务!%@", [NSThread currentThread]);
    [self completed];
}
- (void)completed {
    @synchronized (self) {
        self.executing = NO;
        self.finished = YES;
    }
}
- (void)cancel {
    [super cancel];
    if (self.isStart) {
        [self completed];
    }
}
@end

运行结果:



2021-04-11 11:48:12.601640+0800 OperationQueue[1671:69134] 准备开始执行任务啦~ <NSThread: 0x600000b58380>{number = 8, name = (null)}2021-04-11 11:48:15.605299+0800 OperationQueue[1671:69134] 完成了当前的任务!<NSThread: 0x600000b58380>{number = 8, name = (null)}

源码:https://github.com/gltwy/public

相关文章
|
2月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
223 0
|
22天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
26天前
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
1月前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
57 1
|
2月前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
【10月更文挑战第18天】本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户按下按钮时,按钮将从圆形变为椭圆形并从蓝色渐变为绿色;释放按钮时,动画恢复原状。通过UIView的动画方法和弹簧动画效果,实现平滑自然的动画过渡。
61 5
|
2月前
|
Swift iOS开发 UED
实现一个自定义的iOS动画效果
本文介绍如何使用Swift和UIKit在iOS应用中实现一个自定义按钮动画,当按钮被点击时,其颜色从蓝色渐变为绿色,形状从圆形变为椭圆形,释放后恢复原状。通过UIView动画方法实现这一效果,代码示例展示了动画的平滑过渡和状态切换,有助于提升应用的视觉体验和用户交互。
58 1
|
2月前
|
Java Linux 调度
Java线程的优先级详解
Java线程的优先级机制允许开发者根据程序需求为线程设定不同优先级,范围通常在1到10之间,默认优先级为5。高优先级线程在执行时通常会得到更多的CPU时间,但这并不意味着低优先级线程会被完全忽略。系统资源分配仍然取决于具体的调度策略。理解线程优先级有助于优化多线程应用的性能。
193 8
|
2月前
|
安全 调度 数据安全/隐私保护
iOS线程锁
iOS线程锁
31 0
|
3月前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
3月前
|
Swift iOS开发 UED
揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【9月更文挑战第5天】本文通过具体案例介绍如何在iOS应用中使用Swift与UIKit实现自定义按钮动画,当用户点击按钮时,按钮将从圆形变为椭圆形并从蓝色渐变到绿色,释放后恢复原状。文中详细展示了代码实现过程及动画平滑过渡的技巧,帮助读者提升应用的视觉体验与特色。
70 11