iOS-多個(gè)網(wǎng)絡(luò)請求完成后執(zhí)行下一步操作

在APP開發(fā)中,我們經(jīng)常會(huì)遇到下面兩種需求:

1.同時(shí)做多個(gè)網(wǎng)絡(luò)請求,所有網(wǎng)絡(luò)請求都完成后進(jìn)行下一步的操作。我們暫時(shí)稱之為并發(fā)網(wǎng)路請求
2.依次做多個(gè)網(wǎng)絡(luò)請求,最后一個(gè)網(wǎng)絡(luò)請求完成后進(jìn)行下一步的操作。我們暫時(shí)稱之為依次網(wǎng)路請求
總結(jié)起來如下圖

網(wǎng)絡(luò)請求.jpeg

提供幾種不同的解決方法
  • 并發(fā)網(wǎng)絡(luò)請求解決方法
    1.dispatch_group_t解決
  • 依次網(wǎng)絡(luò)解決方法
    1.NSOperationQueue 線程依賴 + dispatch_semaphore_wait解決
    2.RAC-Concat解決
    3.NSConditionLock解決
    附件:簡單AFNetworking封裝

下面我們以實(shí)際例子作介紹。。。
請求路徑

static NSString *url = @"https://api.douban.com/v2/book/1220562";

一、并發(fā)網(wǎng)絡(luò)請求

1.使用dispatch_group_t解決

1.1:不需要對每一次請求結(jié)果進(jìn)行處理、只需知道什么視乎請求完成

- (void)request1 {
    
    // 不需要對每一次請求結(jié)果進(jìn)行處理、只需知道什么視乎請求完成
    dispatch_group_t group = dispatch_group_create();
    for (int i = 0; i < 3; i++) {
        dispatch_group_enter(group);
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            //請求3
            [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
                dispatch_group_leave(group);
                NSLog(@"請求-Success");
            } failure:^(SPRequest *request, NSError *error) {
                dispatch_group_leave(group);
                NSLog(@"請求-Fail");
                
            }];
        });
    }
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"所有請求完成,進(jìn)行下一步操作");
    });
}

打印結(jié)果:

2018-07-22 10:38:51.010669+0800 LPExample[6614:330141] 請求3-Success
2018-07-22 10:38:51.030527+0800 LPExample[6614:330141] 請求1-Success
2018-07-22 10:38:51.038626+0800 LPExample[6614:330141] 請求2-Success
2018-07-22 10:38:51.038787+0800 LPExample[6614:330141] 所有請求完成,進(jìn)行下一步操作

1.2: 需要對每一次請求結(jié)果進(jìn)行處理、每個(gè)請求完成或者失敗后,處理參數(shù)

- (void)request2 {
    
    // 需要對每一次請求結(jié)果進(jìn)行處理、每個(gè)請求完成或者失敗后,處理參數(shù)
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求1
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            dispatch_group_leave(group);
            NSLog(@"請求1-Success");
            // 對請求1responseObject進(jìn)行處理
        } failure:^(SPRequest *request, NSError *error) {
            dispatch_group_leave(group);
            NSLog(@"請求1-Fail");
            // 對請求1error進(jìn)行處理
        }];
    });
    
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求2
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            dispatch_group_leave(group);
            NSLog(@"請求2-Success");
            // 對請求2responseObject進(jìn)行處理
        } failure:^(SPRequest *request, NSError *error) {
            dispatch_group_leave(group);
            NSLog(@"請求2-Fail");
            // 對請求2error進(jìn)行處理
        }];
    });
    
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //請求3
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            dispatch_group_leave(group);
            NSLog(@"請求3-Success");
            // 對請求3responseObject進(jìn)行處理
        } failure:^(SPRequest *request, NSError *error) {
            dispatch_group_leave(group);
            NSLog(@"請求3-Fail");
            // 對請求3error進(jìn)行處理
        }];
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"所有請求完成,進(jìn)行下一步操作");
    });
}

打印結(jié)果:

2018-07-22 11:59:11.099108+0800 LPExample[7640:386983] 請求2-Success
2018-07-22 11:59:11.112569+0800 LPExample[7640:386983] 請求1-Success
2018-07-22 11:59:11.146874+0800 LPExample[7640:386983] 請求3-Success
2018-07-22 11:59:11.147018+0800 LPExample[7640:386983] 所有請求完成,進(jìn)行下一步操作
使用dispatch_group_enter(group)dispatch_group_leave(group)需注意:

enter和leave必須配合使用,有幾次enter就要有幾次leave,否則group會(huì)一直存在。當(dāng)所有enter的block都leave后,會(huì)執(zhí)行dispatch_group_notify的block。
也可以使用下面判斷

-(void)request {
    NSArray <NSString *>*array = @[@"http://www.weather.com.cn/data/sk/101010100.html",@"https://api.douban.com/v2/book/1220562",@"https://api.apiopen.top/singlePoetry",];
   dispatch_group_t group = dispatch_group_create();
   [array enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
       dispatch_group_enter(group);
       dispatch_async(dispatch_get_global_queue(0, 0), ^{
           //請求3
           [[SPRequest request] GET:obj parameters:nil success:^(SPRequest *request, NSString *responseString) {
               dispatch_group_leave(group);
               if (idx == 0) {
                   //請求0的結(jié)果
               } else if (idx == 1) {
                   //請求1的結(jié)果
               } else if (idx == 2) {
                   //請求2的結(jié)果
               }
               
           } failure:^(SPRequest *request, NSError *error) {
               dispatch_group_leave(group);
               if (idx == 0) {
                   //請求0的結(jié)果
               } else if (idx == 1) {
                   //請求1的結(jié)果
               } else if (idx == 2) {
                   //請求2的結(jié)果
               }
           }];
       });
   }];
   
   dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"\n所有請求完成,進(jìn)行下一步操作");
   });
}

二、 依次網(wǎng)絡(luò)解決方法

1.使用NSOperationQueue 線程依賴 + dispatch_semaphore_wait解決

   - (void)request3 {
    
    // 請求1
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            dispatch_semaphore_signal(sema);
            NSLog(@"請求1-Success");
        } failure:^(SPRequest *request, NSError *error) {
            dispatch_semaphore_signal(sema);
            NSLog(@"請求1-Fail");
        }];
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }];
    
    // 請求2
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            dispatch_semaphore_signal(sema);
            NSLog(@"請求2-Success");
        } failure:^(SPRequest *request, NSError *error) {
            dispatch_semaphore_signal(sema);
            NSLog(@"請求2-Fail");
        }];
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }];
    
    // 請求3
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            dispatch_semaphore_signal(sema);
            NSLog(@"請求3-Success");
        } failure:^(SPRequest *request, NSError *error) {
            dispatch_semaphore_signal(sema);
            NSLog(@"請求3-Fail");
        }];
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }];
    
    // 4.設(shè)置依賴
    [operation2 addDependency:operation1];      //請求2依賴請求1
    [operation3 addDependency:operation2];      //請求3依賴請求2
    
    // 5.創(chuàng)建隊(duì)列并加入任務(wù)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
    
}

打印結(jié)果:

2018-07-22 11:17:12.646628+0800 LPExample[7141:357438] 請求1-Success
2018-07-22 11:17:12.852751+0800 LPExample[7141:357438] 請求2-Success
2018-07-22 11:17:13.053805+0800 LPExample[7141:357438] 請求3-Success

注意:

絕對不要在應(yīng)用主線程中等待一個(gè)Operation,只能在第二或次要線程中等待。阻塞主線程將導(dǎo)致應(yīng)用無法響應(yīng)用戶事件,應(yīng)用也將表現(xiàn)為無響應(yīng)。

2.使用RAC-concat:解決

- (void)request4 {
    // 創(chuàng)建一個(gè)信號(hào)管1
    RACSignal *siganl1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            [subscriber sendNext:@"請求1結(jié)果"];
            [subscriber sendCompleted];
            NSLog(@"請求1-Success");
        } failure:^(SPRequest *request, NSError *error) {
            [subscriber sendCompleted];
            NSLog(@"請求1-Fail");
        }];
        return [RACDisposable disposableWithBlock:^{}];
        
    }];
    
    // 創(chuàng)建一個(gè)信號(hào)管2
    RACSignal *siganl2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            [subscriber sendNext:@"請求2結(jié)果"];
            [subscriber sendCompleted];
            NSLog(@"請求2-Success");
        } failure:^(SPRequest *request, NSError *error) {
            [subscriber sendCompleted];
            NSLog(@"請求2-Fail");
        }];
        return [RACDisposable disposableWithBlock:^{}];
    }];
    
    // 創(chuàng)建一個(gè)信號(hào)管3
    RACSignal *siganl3 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            [subscriber sendNext:@"請求3結(jié)果"];
            [subscriber sendCompleted];
            NSLog(@"請求3-Success");
        } failure:^(SPRequest *request, NSError *error) {
            [subscriber sendCompleted];
            NSLog(@"請求3-Fail");
        }];
        return [RACDisposable disposableWithBlock:^{}];
    }];
    
    // 串聯(lián)管1管2管3
    RACSignal *concatSiganl = [[siganl1 concat:siganl2] concat:siganl3];
    //串聯(lián)后的接收端處理 ,兩個(gè)事件,走兩次,第一個(gè)打印siggnal1的結(jié)果,第二次打印siganl2的結(jié)果
    [concatSiganl subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}

打印結(jié)果:

2018-07-22 11:19:56.889535+0800 LPExample[7185:359636] 請求1結(jié)果
2018-07-22 11:19:56.890303+0800 LPExample[7185:359636] 請求1-Success
2018-07-22 11:19:57.102438+0800 LPExample[7185:359636] 請求2結(jié)果
2018-07-22 11:19:57.103094+0800 LPExample[7185:359636] 請求2-Success
2018-07-22 11:19:57.311119+0800 LPExample[7185:359636] 請求3結(jié)果
2018-07-22 11:19:57.311237+0800 LPExample[7185:359636] 請求3-Success

3.NSConditionLock解決

在所有回調(diào)加入到一個(gè)自定義并發(fā)隊(duì)列中,在隊(duì)列任務(wù)執(zhí)行前進(jìn)行條件加鎖。

- (void)request5 {
    NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];
    dispatch_queue_t queue = dispatch_queue_create("currentQueue", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 3; i++) {
        [[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
            dispatch_async(queue, ^{
                [lock lockWhenCondition:i];
                NSLog(@"請求%@-Success",@(i));
                [lock unlockWithCondition:i+1];
            });
        } failure:^(SPRequest *request, NSError *error) {
            dispatch_async(queue, ^{
                [lock lockWhenCondition:i];
                NSLog(@"請求%@-Fail",@(i));
                [lock unlockWithCondition:i+1];
            });
        }];
    }
}

打印結(jié)果:

2018-07-22 11:44:50.595675+0800 LPExample[7479:377518] 請求0-Success
2018-07-22 11:44:50.595843+0800 LPExample[7479:377514] 請求1-Success
2018-07-22 11:44:50.644479+0800 LPExample[7479:377518] 請求2-Success

參考文章1
參考文章2

附件簡單AFNetworking封裝

#import <Foundation/Foundation.h>

#import <AFNetworking/AFNetworking.h>

@class SPRequest;
@protocol SPRequestDelegate <NSObject>

- (void)SPRequest:(SPRequest *)request finished:(NSDictionary *)response;
- (void)SPRequest:(SPRequest *)request Error:(NSError *)error;

@end

@interface SPRequest : NSObject

@property (assign) id <SPRequestDelegate> delegate;

/**
 *[AFNetWorking]的operationManager對象
 */
@property (nonatomic, strong) AFHTTPSessionManager* operationManager;

/**
 *當(dāng)前的請求operation隊(duì)列
 */
@property (nonatomic, strong) NSOperationQueue* operationQueue;

/**
 *功能: 創(chuàng)建SPRequest的對象方法
 */
+ (instancetype)request;

/**
 *功能:GET請求
 *參數(shù):(1)請求的url: urlString
 *     (2)請求成功調(diào)用的Block: success
 *     (3)請求失敗調(diào)用的Block: failure
 */
- (void)GET:(NSString *)URLString
 parameters:(NSDictionary*)parameters
    success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
    failure:(void (^)(SPRequest *request, NSError *error))failure;

/**
 *功能:POST請求
 *參數(shù):(1)請求的url: urlString
 *     (2)POST請求體參數(shù):parameters
 *     (3)請求成功調(diào)用的Block: success
 *     (4)請求失敗調(diào)用的Block: failure
 */
- (void)POST:(NSString *)URLString
  parameters:(NSDictionary*)parameters
     success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
     failure:(void (^)(SPRequest *request, NSError *error))failure;

/**
 *  post請求
 *
 *  @param URLString  請求網(wǎng)址
 *  @param parameters 請求參數(shù)
 */
- (void)postWithURL:(NSString *)URLString parameters:(NSDictionary *)parameters;

/**
 *  get 請求
 *
 *  @param URLString 請求網(wǎng)址
 */
- (void)getWithURL:(NSString *)URLString;

/**
 *取消當(dāng)前請求隊(duì)列的所有請求
 */
- (void)cancelAllOperations;

@end
#import "SPRequest.h"

@implementation SPRequest

+ (instancetype)request {
    return [[self alloc] init];
}

- (instancetype)init {
    self = [super init];
    if (self) {
        self.operationManager = [AFHTTPSessionManager manager];
        self.operationQueue = self.operationManager.operationQueue;
        
        self.operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
        self.operationManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/json", @"text/plain", @"text/html", nil];
    }
    return self;
}

- (void)GET:(NSString *)URLString
 parameters:(NSDictionary*)parameters
    success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
    failure:(void (^)(SPRequest *request, NSError *error))failure {
    
    [self.operationManager GET:URLString parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        success(self,responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failure(self,error);
    }];
    
}

- (void)POST:(NSString *)URLString
  parameters:(NSDictionary*)parameters
     success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
     failure:(void (^)(SPRequest *request, NSError *error))failure{
    
    [self.operationManager POST:URLString parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        success(self,responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failure(self,error);
    }];
}

- (void)postWithURL:(NSString *)URLString parameters:(NSDictionary *)parameters {
    [self POST:URLString
    parameters:parameters
       success:^(SPRequest *request, NSDictionary *responseObject) {
           if ([self.delegate respondsToSelector:@selector(SPRequest:finished:)]) {
               [self.delegate SPRequest:request finished:responseObject];
           }
       }
       failure:^(SPRequest *request, NSError *error) {
           if ([self.delegate respondsToSelector:@selector(SPRequest:Error:)]) {
               [self.delegate SPRequest:request Error:error];
           }
       }];
}

- (void)getWithURL:(NSString *)URLString {
    
    [self GET:URLString parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
        if ([self.delegate respondsToSelector:@selector(SPRequest:finished:)]) {
            [self.delegate SPRequest:request finished:responseObject];
        }
    } failure:^(SPRequest *request, NSError *error) {
        if ([self.delegate respondsToSelector:@selector(SPRequest:Error:)]) {
            [self.delegate SPRequest:request Error:error];
        }
    }];
}
- (void)cancelAllOperations{
    [self.operationQueue cancelAllOperations];
}
@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,215評(píng)論 8 265
  • 在開發(fā)中,我們很容易遇到這樣的需求,需要我們同時(shí)做多個(gè)網(wǎng)絡(luò)請求,所有網(wǎng)絡(luò)請求都完成后才能進(jìn)行下一步的操作。如下載多...
    _清墨閱讀 19,174評(píng)論 52 142
  • “你先回去吧。”面試官對著阿俊這樣說道。 面試失敗了,阿俊心里已經(jīng)知道了結(jié)果。 自己的面試水平實(shí)在是太差了,上學(xué)期...
    849745bedbe5閱讀 266評(píng)論 0 1
  • 瞧不起別人,想著讓我來肯定更快更好,可實(shí)際結(jié)果是我上了之后追求速度,慌慌張張,結(jié)果就是出了錯(cuò),讓別人給我墊錢計(jì)算,...
    心不塵封閱讀 278評(píng)論 0 0
  • Windows C盤清理指北 動(dòng)機(jī) C盤空間不足和每個(gè)人小時(shí)候都會(huì)尿褲子是一個(gè)道理的尷尬但是卻必須經(jīng)歷,,???,...
    Rayhaha閱讀 1,398評(píng)論 0 2

友情鏈接更多精彩內(nèi)容