在开发过程中,一般一个页面的数据通过一个接口请求来实现,但也有当一个页面分不同模块时,后台通过多个接口实现(其实可以一个接口实现,后台同事觉得后续迭代方便)。
方式一:GCD的三个方法解决
1、dispatch_group_enter():通知group,下面的任务马上要放到group中执行了。
2、dispatch_group_leave():通知group,任务完成了,该任务要从group中移除了。
3、dispatch_group_notify():当所有enter到group里的任务都leaves时执行此通知。
个人理解:和内存管理的引用计数类似,我们可以认为group也持有一个整形变量(只是假设),当调用enter时计数加1,调用leave时计数减1,当计数为0时会调用dispatch_group_notify并且dispatch_group_wait会停止等待;
实现:
网络请求基于对AFNetworking的封装,代码如下:
-(void)loadData{
WeakSelf_Macro;
MBProgressHUD *hud = [Common showCustomHUDAddToView:self.view];
dispatch_group_t group = dispatch_group_create();
// 请求一
dispatch_group_enter(group);
[self loadGoods:^(BOOL isSuccess) {
dispatch_group_leave(group);
}];
// 请求二
dispatch_group_enter(group);
[self loadCredit:^(BOOL isSuccess) {
dispatch_group_leave(group);
}];
// 当上述两个请求结束后,收到通知,在此做后续工作
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[hud hideAnimated:YES];
[weakSelf.tableView.mj_header endRefreshing];
[weakSelf.tableView reloadData];
});
}
// 请求一
- (void)loadGoods:(void(^)(BOOL isSuccess))requestBolck{
WeakSelf_Macro;
[NetworkToolCredit appCredit_queryMemberGoodsUserNo:nil success_callback:^(APIResult *result, NSObject *obj) {
if (result.returnCode.integerValue == KErrorCode_SUCCESSE) {
[weakSelf.dataArr removeAllObjects];
[weakSelf.dataArr addObjectsFromArray:(NSMutableArray *)obj];
}else{
[Common makeToast:result.returnMsg view:weakSelf.view];
}
if (requestBolck) {
requestBolck(result.returnCode.integerValue == KErrorCode_SUCCESSE);
}
} failure:^(NSError *error) {
if (requestBolck) {
requestBolck(NO);
}
}];
}
// 请求二
- (void)loadCredit:(void(^)(BOOL isSuccess))requestBolck{
WeakSelf_Macro;
[NetworkToolCredit appCredit_queryBillUserNo:[UserInfo getCurrentUserNo] success_callback:^(APIResult *result, NSObject *obj) {
if (result.returnCode.integerValue == KErrorCode_SUCCESSE) {
weakSelf.billModel = (CreditIndexBillModel *)obj;
}else{
[Common makeToast:result.returnMsg view:weakSelf.view];
}
if (requestBolck) {
requestBolck(result.returnCode.integerValue == KErrorCode_SUCCESSE);
}
} failure:^(NSError *error) {
if (requestBolck) {
requestBolck(NO);
}
}];
}
本文在此介绍另一种实现线程同步的方式,但不适用于上述情况,原因稍后介绍:
void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
将代码块dispatch_block_t block放入队列dispatch_queue_t queue中执 行;并和调度组dispatch_group_t group相互关联;如果提交到dispatch_queue_t queue中的block全都执行完毕会调用dispatch_group_notify并且dispatch_group_wait会停止等待;
不适用原因:它的判断标准是放入的block是否执行完毕,如果我们放入block中包含异步的网络请求,这个方法无法在网络数据返回后再进行同步。
方式二:信号量dispatch_semaphore_t
信号量类似于锁。
简单来讲 信号量为0则阻塞线程,大于0则不会阻塞。则我们通过改变信号量的值,来控制是否阻塞线程,从而达到线程同步
GCD中的信号量含有三个函数:
dispatch_semaphore_create 创建一个semaphore信号量
dispatch_semaphore_signal 发送一个信号让信号量+1
dispatch_semaphore_wait 如果信号量计数为0则阻塞等待、否则通过。
实现:
网络请求基于对AFNetworking的封装,代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
WeakSelf_Macro;
//按照顺序
NSBlockOperation *operation_1 = [NSBlockOperation blockOperationWithBlock:^{
[weakSelf loadGoods];
[weakSelf loadCredit];
}];
NSBlockOperation *operation_2 = [NSBlockOperation blockOperationWithBlock:^{
[weakSelf.tableView.mj_header endRefreshing];
[weakSelf.tableView reloadData];
}];
//设置依赖
[operation_2 addDependency:operation_1];
//创建队列并添加任务
//如果为YES。阻塞当前线程、直到队列该次添加的所有操作全部执行完成。
//如果为NO。就是批量添加操作而已。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperations:@[operation_2,operation_1] waitUntilFinished:NO];
}
// 请求一
- (void)loadGoods{
//创建信号量并设置计数默认为0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
WeakSelf_Macro;
[NetworkToolCredit appCredit_queryMemberGoodsUserNo:nil success_callback:^(APIResult *result, NSObject *obj) {
if (result.returnCode.integerValue == KErrorCode_SUCCESSE) {
[weakSelf.dataArr removeAllObjects];
[weakSelf.dataArr addObjectsFromArray:(NSMutableArray *)obj];
}else{
[Common makeToast:result.returnMsg view:weakSelf.view];
}
//计数加1
dispatch_semaphore_signal(semaphore);
} failure:^(NSError *error) {
//计数加1
dispatch_semaphore_signal(semaphore);
}];
//若计数为0则一直等待
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
// 请求二
- (void)loadCredit{
//创建信号量并设置计数默认为0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
WeakSelf_Macro;
[NetworkToolCredit appCredit_queryBillUserNo:[UserInfo getCurrentUserNo] success_callback:^(APIResult *result, NSObject *obj) {
if (result.returnCode.integerValue == KErrorCode_SUCCESSE) {
weakSelf.billModel = (CreditIndexBillModel *)obj;
}else{
[Common makeToast:result.returnMsg view:weakSelf.view];
}
//计数加1
dispatch_semaphore_signal(semaphore);
} failure:^(NSError *error) {
//计数加1
dispatch_semaphore_signal(semaphore);
}];
//若计数为0则一直等待
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
方式三:RAC - rac_liftSelector
// 运用这两个API
- (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ...
- (RACSignal *)rac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray *)signals
示例:
- (void)viewDidLoad {
[super viewDidLoad];
RACSignal *goodsSingle = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// 发送请求
[NetworkToolCredit appCredit_queryMemberGoodsUserNo:nil success_callback:^(APIResult *result, NSObject *obj) {
if (result.returnCode.integerValue == KErrorCode_SUCCESSE) {
[subscriber sendNext:@"请求结果"];
}else{
[Common makeToast:result.returnMsg view:weakSelf.view];
}
} failure:^(NSError *error) {
}];
return [[RACDisposable alloc]init];
}];
RACSignal *billSingle = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// 发送请求
[NetworkToolCredit appCredit_queryBillUserNo:[UserInfo getCurrentUserNo] success_callback:^(APIResult *result, NSObject *obj) {
if (result.returnCode.integerValue == KErrorCode_SUCCESSE) {
[subscriber sendNext:@"请求结果"];
}else{
[Common makeToast:result.returnMsg view:weakSelf.view];
}
} failure:^(NSError *error) {
}];
return [[RACDisposable alloc]init];
}];
[self rac_liftSelector:@selector(updateUIWithGoodsData:billData:) withSignals:goodsSingle,billSingle, nil];
}
// 当上述两个请求结束后,在此做后续工作
-(void)updateUIWithGoodsData:(NSString *)goodsData billData:(NSString *)billData{
}