NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性
也更高。
NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程
的能力,只有配合NSOperationQueue才能实现异步执行。
因为NSOperation是基于GCD的,那么使用起来也和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于
GCD中的队列。
NSOperation实现多线程的使用步骤分为三步:
(1)创建任务:先将需要执行的操作封装到一个NSOperation对象中。
(2)创建队列:创建NSOperationQueue对象。
(3)将任务加入到队列中:然后将NSOperation对象添加到NSOperationQueue中。
之后系统就会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作。
1、NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。
(1)凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行。
(2)添加到其他队列中的任务(NSOperation),就会自动放到子线程中执行,且 同时包含了:串行、并发功能。
2、队列中的最大并发数maxConcurrentOperationCount(默认为-1)
(1)当maxConcurrentOperationCount为-1,表示不进行限制,默认为并发执行。
(2)当maxConcurrentOperationCount为1时,进行串行执行。
(3)当maxConcurrentOperationCount大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整。
3、队列中的任务间的依赖
比如说有A、B两个操作,其中A执行完操作,B才能执行操作,那么就需要让B依赖于A。
(1)不要建立循环依赖,会造成死锁,原因同循环引用
(2)使用依赖建议只使用NSInvocationOperation,NSInvocationOperation和NSBlockOperation混用会导致依赖关系无法正常实现。
(3)依赖关系不光在同队列中生效,不同队列的NSOperation对象之前设置的依赖关系一样会生效
4、优先级,建议使用默认级别
5、取消等操作
- (void)cancel; NSOperation提供的方法,可取消单个操作
- (void)cancelAllOperations; NSOperationQueue提供的方法,可以取消队列的所有操作,所有任务取消,包括正在执行的,还未执行的。
- (void)setSuspended:(BOOL)b; 可设置任务的暂停和恢复,YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended; 判断暂停状态,暂停是指当前队列中正在执行,或还未执行的任务;恢复是指继续执行暂停后的任务。
注意:
(1)单独使用任务NSOperation时,是在主线程中同步执行的。
(2)NSInvocationOperation实例必须加到 NSOperationQueue 的方法" - (void)addOperation:(NSOperation *)op; "中才能开始执行.
(3)NSInvocationOperation子线程的停止,可通过 NSOperationQueue 的方法" - (void)cancelAllOperations; "停止。
代码示例
1、同步任务
// 同步,在主线程中执行
- (void)runSynchronization
{
// 创建任务
// 方法1
// 使用子类 NSInvocationOperation 对象,在主线程中执行
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@110];
[operation1 start];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@120];
[operation2 start];
}
- (void)showTime:(id)object
{
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 17:59:44.794 DemoThread[7885:1445277] 主线程:1, 线程名称:{number = 1, name = main}, object = 110, time 9.000000
2018-03-06 17:59:49.796 DemoThread[7885:1445277] 主线程:1, 线程名称:{number = 1, name = main}, object = 120, time 5.000000
// 同步,在主线程中执行
- (void)runSynchronization
{
// 创建任务
// 使用子类 NSBlockOperation 对象,在主线程中执行
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
[self showTime:@210];
}];
[operation1 addExecutionBlock:^{
// 并行,开辟子线程,在子线程中执行
[self showTime:@2110];
}];
[operation1 addExecutionBlock:^{
// 并行,开辟子线程,在子线程中执行
[self showTime:@2120];
}];
[operation1 start];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
[self showTime:@220];
}];
[operation2 addExecutionBlock:^{
// 并行,开辟子线程,在子线程中执行
[self showTime:@2210];
}];
[operation2 addExecutionBlock:^{
// 并行,开辟子线程,在子线程中执行
[self showTime:@2220];
}];
[operation2 start];
}
- (void)showTime:(id)object
{
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 17:56:23.772 DemoThread[7847:1441955] 主线程:0, 线程名称:{number = 3, name = (null)}, object = 2110, time 2.000000
2018-03-06 17:56:26.697 DemoThread[7847:1441684] 主线程:0, 线程名称:{number = 4, name = (null)}, object = 2120, time 5.000000
2018-03-06 17:56:29.699 DemoThread[7847:1441602] 主线程:1, 线程名称:{number = 1, name = main}, object = 210, time 8.000000
2018-03-06 17:56:29.699 DemoThread[7847:1441684] 主线程:0, 线程名称:{number = 4, name = (null)}, object = 2210, time 0.000000
2018-03-06 17:56:32.768 DemoThread[7847:1441955] 主线程:0, 线程名称:{number = 3, name = (null)}, object = 2220, time 3.000000
2018-03-06 17:56:38.701 DemoThread[7847:1441602] 主线程:1, 线程名称:{number = 1, name = main}, object = 220, time 9.000000
2、主队列,在主线程中执行
// 主队列,在主线程中执行
- (void)runMainQueue
{
// 1 创建队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
[queue addOperationWithBlock:^{
[self showTime:@210];
}];
[queue addOperationWithBlock:^{
[self showTime:@220];
}];
}
- (void)showTime:(id)object
{
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 18:01:30.895 DemoThread[7918:1447374] 主线程:1, 线程名称:{number = 1, name = main}, object = 210, time 2.000000
2018-03-06 18:01:35.897 DemoThread[7918:1447374] 主线程:1, 线程名称:{number = 1, name = main}, object = 220, time 5.000000
// 主队列,在主线程中执行
- (void)runMainQueue
{
// 1 创建队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
// 2 创建任务,不需要调用任务的开始命令start
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@110];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@120];
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
[self showTime:@220];
}];
[operation3 addExecutionBlock:^{
// 并行,开辟子线程,在子线程中执行
[self showTime:@2210];
}];
// 3 添加到队列中
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
}
- (void)showTime:(id)object
{
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 18:03:42.451 DemoThread[7949:1449547] 主线程:1, 线程名称:{number = 1, name = main}, object = 110, time 7.000000
2018-03-06 18:03:46.453 DemoThread[7949:1449547] 主线程:1, 线程名称:{number = 1, name = main}, object = 120, time 4.000000
2018-03-06 18:03:47.455 DemoThread[7949:1449547] 主线程:1, 线程名称:{number = 1, name = main}, object = 220, time 1.000000
2018-03-06 18:03:49.454 DemoThread[7949:1450059] 主线程:0, 线程名称:{number = 3, name = (null)}, object = 2210, time 3.000000
3、其他队列,开辟子线程执行
// 定义变量
NSOperationQueue *queueOther;
// 其他队列,开辟新的子线程
- (void)runOtherQueue
{
// 1 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"queue other";
// 优先级
queue.qualityOfService = NSQualityOfServiceDefault;
// 最大并发数(-1表示不进行限制,默认为并发执行;1表示串行执行;大于1时,进行并发执行)
queue.maxConcurrentOperationCount = 5;
// // 2 创建任务,不需要调用任务的开始命令start
// NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@110];
// operation1.name = @"operation1";
// NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@120];
// operation2.name = @"operation2";
// NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
// [self showTime:@220];
// }];
// operation3.name = @"operation3";
// [operation3 addExecutionBlock:^{
// // 并行,开辟子线程,在子线程中执行
// [self showTime:@2210];
// }];
// // 依赖关系
// [operation1 addDependency:operation2];
// [operation3 addDependency:operation1];
//
// // 3 添加到队列中
// [queue addOperation:operation1];
// [queue addOperation:operation2];
// [queue addOperation:operation3];
// 或3(省略2)
// [queue addOperationWithBlock:^{
// [self showTime:@210];
// }];
// [queue addOperationWithBlock:^{
// [self showTime:@220];
// }];
// 多任务
for (int i = 0; i < 1000; i++)
{
//
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@(i + 1)];
[queue addOperation:operation];
// 或
// [queue addOperationWithBlock:^{
// [self showTime:@(i + 1)];
// }];
}
//
NSLog(@"start: %ld, %ld", queue.operationCount, queue.operations.count);
queueOther = queue;
}
- (void)showTime:(id)object
{
if (((NSNumber *)object).integerValue == 10)
{
NSInvocationOperation *operation = [queueOther.operations objectAtIndex:9];
[operation cancel];
if ([operation isCancelled])
{
NSLog(@"已经退出执行");
return;
}
}
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 18:05:10.013 DemoThread[7971:1451469] 主线程:0, 线程名称:{number = 3, name = (null)}, object = 1, time 0.000000
2018-03-06 18:05:10.013 DemoThread[7971:1451484] 主线程:0, 线程名称:{number = 4, name = (null)}, object = 4, time 0.000000
2018-03-06 18:05:10.023 DemoThread[7971:1451407] start: 998, 998
2018-03-06 18:05:16.018 DemoThread[7971:1451587] 主线程:0, 线程名称:{number = 5, name = (null)}, object = 6, time 6.000000
2018-03-06 18:05:17.017 DemoThread[7971:1451484] 主线程:0, 线程名称:{number = 4, name = (null)}, object = 7, time 7.000000
2018-03-06 18:05:17.018 DemoThread[7971:1451484] 主线程:0, 线程名称:{number = 4, name = (null)}, object = 9, time 0.000000
2018-03-06 18:05:17.019 DemoThread[7971:1451484] 已经退出执行
2018-03-06 18:05:18.016 DemoThread[7971:1451466] 主线程:0, 线程名称:{number = 6, name = (null)}, object = 5, time 8.000000
2018-03-06 18:05:18.421 DemoThread[7971:1451407] pause: 993, 993
2018-03-06 18:05:19.016 DemoThread[7971:1451485] 主线程:0, 线程名称:{number = 7, name = (null)}, object = 2, time 9.000000
2018-03-06 18:05:19.016 DemoThread[7971:1451467] 主线程:0, 线程名称:{number = 8, name = (null)}, object = 3, time 9.000000
2018-03-06 18:05:21.022 DemoThread[7971:1451469] 主线程:0, 线程名称:{number = 3, name = (null)}, object = 8, time 5.000000
2018-03-06 18:05:21.253 DemoThread[7971:1451407] pause: 990, 990
2018-03-06 18:05:22.019 DemoThread[7971:1451466] 主线程:0, 线程名称:{number = 6, name = (null)}, object = 12, time 4.000000
2018-03-06 18:05:22.917 DemoThread[7971:1451407] cancle: 988, 988
- (void)pauseClick
{
if (queueOther)
{
// 暂停或继续
NSLog(@"pause: %ld, %ld", queueOther.operationCount, queueOther.operations.count);
queueOther.suspended = !queueOther.isSuspended;
}
}
- (void)cancleClick
{
if (queueOther)
{
// 停止
NSLog(@"cancle: %ld, %ld", queueOther.operationCount, queueOther.operations.count);
[queueOther cancelAllOperations];
}
}
图片下载示例
bool isCancelOperation = NO;
- (void)stopClick
{
if (self.queue)
{
NSLog(@"1 停止");
isCancelOperation = YES;
[self.queue cancelAllOperations];
}
}
- (void)showCount:(NSNumber *)number
{
NSInteger count = arc4random() % 1000;
for (int i = 0; i < count; i++)
{
NSLog(@"第 %@ 个 i = %@", number, @(i));
// 休眠n秒再执行
[NSThread sleepForTimeInterval:0.2];
// 停止
if (isCancelOperation)
{
NSLog(@"2 停止");
break;
}
}
}
- (void)downloadImage:(NSString *)imageUrl
{
NSURL *url = [NSURL URLWithString:imageUrl];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
if (image == nil)
{
}
else
{
// [self performSelectorOnMainThread:@selector(updateImage:) withObject:image waitUntilDone:YES];
[self performSelectorInBackground:@selector(updateImage:) withObject:image];
}
}
- (void)updateImage:(UIImage *)image
{
self.imageview.image = image;
}
NSString *imageUrl = @"http://ww1.sinaimg.cn/crop.0.0.1242.1242.1024/763fb12bjw8empveq3eq8j20yi0yiwhw.jpg";
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downloadImage:) object:imageUrl];
self.queue = [[NSOperationQueue alloc] init];
[self.queue addOperation:operation];
NSInvocationOperation *operationCount1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(showCount:) object:@(11)];
[self.queue addOperation:operationCount1];
NSInvocationOperation *operationCount2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(showCount:) object:@(12)];
[self.queue addOperation:operationCount2];