场景:一个页面有多个接口 需要等多个接口全都请求完成之后再刷新UI reloadData.
1:串行请求,接口嵌套 耗时久,有点low。
2:dispatch_group
用dispatch_after模拟接口耗时。
方式1: dispatch_group_notify
方式2: dispatch_group_wait(会阻塞当前线程不要放主线程用)
方式3:用信号量堵塞请求任务 请求完成才增加信号量完成任务
如果直接如下:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"????2");
});
});
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"????1");
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"任务已完成");
});
打印结果
2019-11-22 15:26:27.846570+0800 11[17701:770534] 任务已完成
2019-11-22 15:26:30.827562+0800 11[17701:770534] ????2
2019-11-22 15:26:37.827784+0800 11[17701:770534] ????1
因为dispatch_after是异步瞬间完成 需要配合leavegroup entergroup来用
更改如下:
方式1:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"????2");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"????1");
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"任务已完成");
});
log如下
2019-11-22 15:37:06.231599+0800 11[17731:774655] ????2
2019-11-22 15:37:13.231893+0800 11[17731:774655] ????1
2019-11-22 15:37:13.232329+0800 11[17731:774655] 任务已完成
方式2:
[self performSelectorInBackground:@selector(test2) withObject:nil];
- (void)test2 {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"????2");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"????1");
dispatch_group_leave(group);
});
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"任务已完成");
}
方式3:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
[self request1];
});
dispatch_group_async(group, queue, ^{
[self request2];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"任务已完成");
});
}
- (void)request1{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"请求1完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
- (void)request2{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"请求2完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
场景:上百张图片每10张10张上传
信号量方式 用信号量堵塞异步数据请求直到请求完成
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//信号量初始化为10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
//同时执行100个任务
for (int i = 0; i < 100; i++)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//可用信号量-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务%d执行",i+1);
[self request1];
//可用信号量+1
dispatch_semaphore_signal(semaphore);
});
}
}
- (void)request2{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"请求2完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
场景:A请求依赖B请求 其中B是异步操作 用addDependency来实现 (这个是练习理解 自定义Operation 可以配合KVO在合适时机调用isFinished )
同样可以用来做AB接口请求同步 用GCD想取消的话不如这个方便
参考自:https://www.jianshu.com/p/e1b08b8bd5af
作者:Baoger
代码示例如下
.h
自定义operation
@class CustomOperation;
/// 使用delegate在外面完成对应的操作
@protocol CustomOperationDelegate <NSObject>
-(void)startOperation:(CustomOperation *)operation;
@end
@property (nonatomic, copy, readonly) NSString *taskName;
@property(nonatomic, weak) id <CustomOperationDelegate>delegate;
- (instancetype)initWithTaskName:(NSString *)taskName;
/// 操作完成时候外部调用改变状态
-(void)completeOperation;
.m
#import "CustomOperation.h"
@interface CustomOperation ()
/// 是否正在进行
@property (nonatomic, assign) BOOL bees_executing;
/// 是否完成
@property (nonatomic, assign) BOOL bees_finished;
@end
@implementation CustomOperation
-(instancetype)initWithTaskName:(NSString *)taskName {
if (self = [super init]) {
_bees_executing = NO;
_bees_finished = NO;
_taskName = taskName;
// NSLog(@"%@ CustomOperation alloc %@", _taskName,self);
}
return self;
}
- (void)dealloc {
NSLog(@"%@ dealloc %@", self.taskName,self);
}
- (void)start {
if (self.isCancelled) {
// 若当前操作为取消,则结束操作,且要修改isExecuting和isFinished的值,通过kvo的方式告诉对应的监听者其值的改变
NSLog(@"%@ cancel %@", self.taskName,self);
[self completeOperation];
} else {
// 正在执行操作
self.bees_executing = YES;
// 通过代理,在外部实现对应的异步操作
if (self.delegate && [self.delegate respondsToSelector:@selector(startOperation:)]) {
[self.delegate startOperation:self];
}
}
}
/// 结束当前操作,改变对应的状态
- (void)completeOperation {
self.bees_executing = NO;
self.bees_finished = YES;
}
// 一定要重写cancel方法,结束状态
- (void)cancel {
[super cancel];
// 取消后一定要调用完成,删除queue中的operation
[self completeOperation];
}
#pragma mark - settter and getter
// setter 修改自己状态的同时,发送父类对应属性状态改变的kvo通知
- (void)setBees_executing:(BOOL)bees_executing {
[self willChangeValueForKey:@"isExecuting"];
_bees_executing = bees_executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (void)setBees_finished:(BOOL)bees_finished {
[self willChangeValueForKey:@"isFinished"];
_bees_finished = bees_finished;
[self didChangeValueForKey:@"isFinished"];
}
// 父类返回自己维护的对应的状态
- (BOOL)isExecuting {
return self.bees_executing;
}
- (BOOL)isFinished {
return self.bees_finished;
}
- (BOOL)isAsynchronous {
return true;
}
//调用
- (void)viewDidLoad {
[super viewDidLoad];
CustomOperation *uploadOperation = [[CustomOperation alloc]initWithTaskName:@"test"];
uploadOperation.delegate = self;
NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"33333");
}];
[block addDependency:uploadOperation];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
queue.maxConcurrentOperationCount = 4;
[queue addOperation:uploadOperation];
[queue addOperation:block];
return;
}
- (void)startOperation:(CustomOperation *)operation {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"第一个任务完毕");
[operation completeOperation];///这儿很关键 手动标识任务finished
});
return;
}
@end
进阶参考:1.概论
An operation object is a single-shot object—that is, it executes its task once
and cannot be used to execute it again. You typically execute operations by
adding them to an operation queue (an instance of
the [NSOperation<wbr>Queue
](apple-reference-
documentation://hcOi0eohIY) class). An operation queue executes its
operations either directly, by running them on secondary threads, or
indirectly using the libdispatch
library (also known as Grand Central
Dispatch). For more information about how queues execute operations,
see [NSOperation<wbr>Queue
](apple-reference-
documentation://hcOi0eohIY).
If you do not want to use an operation queue, you can execute an operation
yourself by calling its start
method directly from your code. Executing
operations manually does put more of a burden on your code, because
starting an operation that is not in the ready state triggers an exception.
The ready
property reports on the operation’s readiness.
NSOperation可以通过start方法直接调用,但是更好的方法是将其放入NSOperationQueue中。
放入NSOperationQueue中的NSOperation会在合适的时机自动执行,如果没有依赖和并发数量限制,则会立刻执行(调用start方法),在NSOperationQueue中的NSOperation默认异步执行。
cancelAllOperations可以将NSOperationQueue中所有的NSOperation发送cancel的消息。
2.异步和同步
一个独立的Operation 默认是同步的,且在当前调用的线程中执行start方法。
When you call the start method of a synchronous operation directly from
your code, the operation executes immediately in the current thread.
asynchronous是一个控制异步同步的属性,YES可以让NSOperation异步执行,但是当Operation放入OperationQueue中时,asynchronous就失去意义了,Operation默认会变成异步。
- 非并发的NSOperation
非并发的NSOperation意味着是NSOperation中没有并发的操作,执行完成后这个NSOperation就失效了(状态变为isFinished)。 对于非并发的NSOperation,只需要覆盖main方法,在main中做操作,NSOperation会自动管理状态,比如在main开始之前将状态设置成isReading,在main方法调用完成将状态设置成isFinished。
For non-concurrent operations, you typically override only one method:
Into this method, you place the code needed to perform the given task. Of
course, you should also define a custom initialization method to make it
easier to create instances of your custom class. You might also want to
define getter and setter methods to access the data from the operation.
However, if you do define custom getter and setter methods, you must make
sure those methods can be called safely from multiple threads.
- 并发的NSOperation
对于有并发操作的NSOperation,需要手动管理状态。并且需要覆盖start方法。 NSOperation的状态
isReady
NSOperation准备好执行时候的状态,通常由于NSOperationQueue并发数的限制和NSOperation依赖的影响,NSOperation不总是立刻进入ready状态。
isExecuting
If you replace the start method of your operation object, you must also replace
the executing property and generate KVO notifications when the execution
state of your operation changes.
当你自定义并发operation的时候,executing的状态是需要自己通过KVO控制的。
-(void)setExecuting:(BOOL)executing{
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];}
isFinished
isFinished 表明当前的operation完成,这样基于此operation依赖的operation才会执行,如果当operation被添加进queue中,当operation变为finished时,将会被自动移除queue,并且释放掉。 当你自定义并发operation的时候,finished的状态是需要自己通过KVO控制的。
-(void)setFinished:(BOOL)executing{
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
isCancelled
isCancelled可以帮助我们在必要的时候枝减掉某些操作,在并发任务中获取isCancelled的状态决定是否继续接下来的操作。
转自:https://m.meiwen.com.cn/subject/djwskftx.html
网友评论