在多线程开发中,我们常用到GCD,这里探讨一下GCD任务的取消:
1.在iOS 8以后,系统给我们提供了这样的取消函数 dispatch_block_cancel,不过这个也只能用于dispatch_block_create创建的dispatch_block_t,我们试验一下:
-(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);
}
这时肯定是任务都会执行的
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);打开,看看效果:
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,这个时候我们若想取消一个任务,可以考虑用一个条件来做,满足条件则执行此任务,不满足则不执行,举个例子:
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;
}
效果如下:
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)
int a = 10;
void (^blcok)() = [^{
NSLog(@"%d",a);
} copy];
a=20;
blcok(); // log : a = 10
3.过渡到NSOperation
NSOperation是对GCD的封装,底层也是GCD。
NSOperation给我们封装了更多的api,这是我在Xcode中提出来的:
@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:
-(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];
}
这时输出是:
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)
-(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];
}
网友评论