iOS多线程GCD任务取消->NSOperation

2019-04-17 17:00:14 浏览数 (1)

在多线程开发中,我们常用到GCD,这里探讨一下GCD任务的取消: 1.在iOS 8以后,系统给我们提供了这样的取消函数 dispatch_block_cancel,不过这个也只能用于dispatch_block_create创建的dispatch_block_t,我们试验一下:

代码语言:javascript复制
-(void)GCD_cancel{
    dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_block_t block1 = dispatch_block_create(0, ^{
        NSLog(@"1");
    });
    
    dispatch_block_t block2 = dispatch_block_create(0, ^{
        NSLog(@"2");
    });
    
    dispatch_block_t block3 = dispatch_block_create(0, ^{
        NSLog(@"3");
    });
    
    dispatch_async(queue, block1);
    dispatch_async(queue, block2);
    dispatch_async(queue, block3);
    
//    dispatch_block_cancel(block1);
    
}

这时肯定是任务都会执行的

代码语言:javascript复制
2019-04-03 14:11:14.896059 0800 Timer[8755:2877034] 1
2019-04-03 14:11:14.896063 0800 Timer[8755:2877035] 2
2019-04-03 14:11:14.896064 0800 Timer[8755:2877036] 3

接下来,把注释的那一行 dispatch_block_cancel(block1);打开,看看效果:

代码语言:javascript复制
2019-04-03 14:12:27.749116 0800 Timer[8776:2877773] 2
2019-04-03 14:12:27.749116 0800 Timer[8776:2877770] 3

我们发现block1确实被取消掉了。这是dispatch_block_cancel的用法。

2.很多时候,我们的场景不会去用dispatch_block_create创建dispatch_block_t,这个时候我们若想取消一个任务,可以考虑用一个条件来做,满足条件则执行此任务,不满足则不执行,举个例子:

代码语言:javascript复制
static BOOL sholdCancel = NO;

-(void)GCD_cancel{
    dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"任务已经开始了");
        for (int i=0; i<100; i  ) {
            if (sholdCancel) {
                NSLog(@"在i=%d的时候已经取消了",i);
                break;
            }
            NSLog(@"%d",i);
            sleep(1);
        }
        
    });
    
    [self performSelector:@selector(GCD_shouldCancel) withObject:nil afterDelay:5.0];
    
}

-(void)GCD_shouldCancel{
    sholdCancel = YES;
}

效果如下:

代码语言:javascript复制
2019-04-03 15:07:49.379672 0800 Timer[9444:2906947] 任务已经开始了
2019-04-03 15:07:49.379872 0800 Timer[9444:2906947] 0
2019-04-03 15:07:50.383439 0800 Timer[9444:2906947] 1
2019-04-03 15:07:51.386239 0800 Timer[9444:2906947] 2
2019-04-03 15:07:52.388414 0800 Timer[9444:2906947] 3
2019-04-03 15:07:53.389778 0800 Timer[9444:2906947] 4
2019-04-03 15:07:54.394204 0800 Timer[9444:2906947] 在i=5的时候已经取消了

写到这里,这儿其实还隐藏了一个知识点,就是block的变量捕获,有兴趣或是不理解的朋友可以研究一下。(如下,为何输出不是20而是10)

代码语言:javascript复制
int a = 10;
void (^blcok)() = [^{
    NSLog(@"%d",a);
} copy];

a=20;

blcok(); // log : a = 10

3.过渡到NSOperation NSOperation是对GCD的封装,底层也是GCD。 NSOperation给我们封装了更多的api,这是我在Xcode中提出来的:

代码语言:javascript复制
@interface NSOperation : NSObject {
@private
    id _private;
    int32_t _private1;
#if __LP64__
    int32_t _private1b;
#endif
}

- (void)start;
- (void)main;

@property (readonly, getter=isCancelled) BOOL cancelled;
- (void)cancel;

@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isConcurrent) BOOL concurrent; // To be deprecated; use and override 'asynchronous' below
@property (readonly, getter=isAsynchronous) BOOL asynchronous API_AVAILABLE(macos(10.8), ios(7.0), watchos(2.0), tvos(9.0));
@property (readonly, getter=isReady) BOOL ready;

- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;

@property (readonly, copy) NSArray<NSOperation *> *dependencies;

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

@property NSOperationQueuePriority queuePriority;

@property (nullable, copy) void (^completionBlock)(void) API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

- (void)waitUntilFinished API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

@property double threadPriority API_DEPRECATED("Not supported", macos(10.6,10.10), ios(4.0,8.0), watchos(2.0,2.0), tvos(9.0,9.0));

@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

@end

我们可以发现它有状态属性,有取消方法,也有添加依赖方法等...这里我们还是先说取消吧,下面来给大家写个demo:

代码语言:javascript复制
-(void)operationCancel{
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1");
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2");
    }];
    
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3");
    }];
    
    [operation1 start];
    [operation2 start];
    [operation3 start];
    
    [operation1 cancel];
}

这时输出是:

代码语言:javascript复制
2019-04-03 14:51:44.512940 0800 Timer[9248:2898426] 1
2019-04-03 14:51:44.513114 0800 Timer[9248:2898426] 2
2019-04-03 14:51:44.513213 0800 Timer[9248:2898426] 3

因为正在执行的任务,NSOperation也是不能取消的,所以也是需要将cancel在start前调用的(就如同满足一个条件是否需要cancel一样,也可以满足条件不调用start)

代码语言:javascript复制
-(void)operationCancel{
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1");
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2");
    }];
    
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"3");
    }];
    
    [operation1 cancel];  //这里调用
    
    [operation1 start];
    [operation2 start];
    [operation3 start];
    
//    [operation1 cancel];
    
}

0 人点赞