用 NSURLSession 替换同步 NSURLConnection 的最佳实践
Best practice to replace a synchronous NSURLConnection with NSURLSession
作为
NSURLConnection sendSynchronousRequest:returningResponse:error:&connectionError
设置为弃用我将不得不替换我很久以前写的导入器。
导入器执行以下操作:
- 它从API-A 获取数据。那里的数据可以在多个页面上。
- 它使用第一次获取的数据(也是多页)从 API-B 查询数据并合并
- 来自 API-B 查询的结果将与来自 API-A
的数据合并
我通过后台操作实现了这一点,其中我为每个 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;
}
作为
NSURLConnection sendSynchronousRequest:returningResponse:error:&connectionError
设置为弃用我将不得不替换我很久以前写的导入器。
导入器执行以下操作:
- 它从API-A 获取数据。那里的数据可以在多个页面上。
- 它使用第一次获取的数据(也是多页)从 API-B 查询数据并合并
- 来自 API-B 查询的结果将与来自 API-A 的数据合并
我通过后台操作实现了这一点,其中我为每个 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;
}