用 NSURLSession 替换同步 NSURLConnection 的最佳实践

Best practice to replace a synchronous NSURLConnection with NSURLSession

作为

NSURLConnection sendSynchronousRequest:returningResponse:error:&connectionError

设置为弃用我将不得不替换我很久以前写的导入器。

导入器执行以下操作:

  1. 它从API-A 获取数据。那里的数据可以在多个页面上。
  2. 它使用第一次获取的数据(也是多页)从 API-B 查询数据并合并
  3. 来自 API-B 查询的结果将与来自 API-A
  4. 的数据合并

我通过后台操作实现了这一点,其中我为每个 API 使用方法,如果请求有多个页面,则递归调用。

但是由于 NSURLSession 不支持同步请求,我目前只看到有很多开销的选项(例如 iVars)控制完成块中调用的内容(例如下一页或开始查询 API-B ).

那么,将其引入 NSURLSession 的优雅解决方案是什么。

注意:只是为了确保,我以前的解决方案根本不会阻塞主线程。但在当时,这是控制两个源合并的最简单方法。

这个答案不应该是最佳实践。这对我来说很实用。

面对在后台执行一堆同步请求并且执行顺序很重要的情况,我最终使用了以下内容:

SyncRequestSender.h

#import <Foundation/Foundation.h>

@interface SyncRequestSender : NSObject

+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request
                 returningResponse:(NSURLResponse **)response
                             error:(NSError **)error;

@end

SyncRequestSender.m

#import "SyncRequestSender.h"

@implementation SyncRequestSender

+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request
                 returningResponse:(NSURLResponse **)response
                             error:(NSError **)error
{
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);


    NSError __block *err = NULL;
    NSData __block *data;
    NSURLResponse __block *resp;

    [[[NSURLSession sharedSession] dataTaskWithRequest:request
                                     completionHandler:^(NSData* _data, NSURLResponse* _response, NSError* _error) {
        resp = _response;
        err = _error;
        data = _data;
        dispatch_group_leave(group);

    }] resume];

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

    if (response)
    {
        *response = resp;
    }
    if (error)
    {
        *error = err;
    }

    return data;
}

@end

这是 AFNetworking 中的示例,它展示了如何等待异步任务。

- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
    __block NSArray *tasks = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
            tasks = dataTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
            tasks = uploadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
            tasks = downloadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
            tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
        }

        dispatch_semaphore_signal(semaphore);
    }];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return tasks;
}