iOS多线程编程——NSOperation的使用

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取出来,在新线程中执行操作。


1NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。

(1)凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行。

(2)添加到其他队列中的任务(NSOperation),就会自动放到子线程中执行,且 同时包含了:串行、并发功能。

2、队列中的最大并发数maxConcurrentOperationCount(默认为-1

(1)当maxConcurrentOperationCount-1,表示不进行限制,默认为并发执行。

(2)当maxConcurrentOperationCount1时,进行串行执行。

(3)当maxConcurrentOperationCount大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整。

3、队列中的任务间的依赖

比如说有AB两个操作,其中A执行完操作,B才能执行操作,那么就需要让B依赖于A

1)不要建立循环依赖,会造成死锁,原因同循环引用

2)使用依赖建议只使用NSInvocationOperationNSInvocationOperationNSBlockOperation混用会导致依赖关系无法正常实现。

3)依赖关系不光在同队列中生效,不同队列的NSOperation对象之前设置的依赖关系一样会生效

4、优先级,建议使用默认级别

5、取消等操作

- (void)cancel; NSOperation提供的方法,可取消单个操作

- (void)cancelAllOperations; NSOperationQueue提供的方法,可以取消队列的所有操作,所有任务取消,包括正在执行的,还未执行的

- (void)setSuspended:(BOOL)b; 可设置任务的暂停和恢复,YES代表暂停队列,NO代表恢复队列

- (BOOL)isSuspended; 判断暂停状态,暂停是指当前队列中正在执行,或还未执行的任务;恢复是指继续执行暂停后的任务


注意:

(1)单独使用任务NSOperation时,是在主线程中同步执行的。

2NSInvocationOperation实例必须加到 NSOperationQueue 的方法" - (void)addOperation:(NSOperation *)op; "中才能开始执行.

3NSInvocationOperation子线程的停止,可通过 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];

iOS多线程编程——NSOperation的使用_第1张图片





你可能感兴趣的:(iOS,多线程)