对同步网络请求最严厉的批评是留给那些从主队列执行同步请求的人的(因为我们知道,永远不应该阻塞主队列).但您是在自己的后台队列上完成的,它解决了同步请求中最严重的问题.但是你正在失go 异步技术提供的一些出色的特性(例如,如果需要,取消请求).
我将在下面回答您的问题(如何让NSURLSessionDataTask
同步运行),但我真的鼓励您接受异步模式,而不是与它们对抗.我建议重构代码,使用异步模式.具体来说,如果一个任务依赖于另一个任务,只需将依赖任务的启动放在前一个任务的完成处理程序中即可.
如果您在转换过程中遇到问题,请发布另一个堆栈溢出问题,向我们展示您的try ,我们可以try 帮助您解决问题.
如果要使异步操作同步,一种常见的模式是使用分派信号量,以便启动异步进程的线程可以在继续之前等待异步操作完成块发出的信号.永远不要从主队列执行此操作,但如果您是从某个后台队列执行此操作,它可能是一种有用的模式.
可以使用以下命令创建信号量:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
然后,可以让异步进程的完成块向信号量发送以下信号:
dispatch_semaphore_signal(semaphore);
然后,您可以让代码在完成块之外(但仍在后台队列,而不是主队列)等待该信号:
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
所以,加上NSURLSessionDataTask
个,这可能看起来像:
[queue addOperationWithBlock:^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSession *session = [NSURLSession sharedSession]; // or create your own session with your own NSURLSessionConfiguration
NSURLSessionTask *task = [session dataTaskWithURL:url completionH和ler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (data) {
// do whatever you want with the data here
} else {
NSLog(@"error = %@", error);
}
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// but have the thread wait until the task is done
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// now carry on with other stuff contingent upon what you did above
]);
对于NSURLConnection
(现在已弃用),您必须跳过一些限制才能从后台队列启动请求,但NSURLSession
可以优雅地处理它.
话虽如此,使用这样的块操作意味着这些操作不会响应取消事件(至少在它们运行时).因此,我通常使用块操作来避免这种信号量技术,而只是将数据任务包装在异步NSOperation
子类中.这样你就可以享受运营带来的好处,但你也可以取消运营.这是更多的工作,但更好的模式.
例如:
//
// DataTaskOperation.h
//
// Created by Robert Ryan on 12/12/15.
// Copyright © 2015 Robert Ryan. All rights reserved.
//
@import Foundation;
#import "AsynchronousOperation.h"
NS_ASSUME_NONNULL_BEGIN
@interface DataTaskOperation : AsynchronousOperation
/// Creates a operation that retrieves the contents of a URL based on the specified URL request object, 和 calls a h和ler upon completion.
///
/// @param request A NSURLRequest object that provides the URL, cache policy, request type, body data or body stream, 和 so on.
/// @param dataTaskCompletionH和ler The completion h和ler to call when the load request is complete. This h和ler is executed on the delegate queue. This completion h和ler takes the following parameters:
///
/// @returns The new session data operation.
- (instancetype)initWithRequest:(NSURLRequest *)request dataTaskCompletionH和ler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionH和ler;
/// Creates a operation that retrieves the contents of a URL based on the specified URL request object, 和 calls a h和ler upon completion.
///
/// @param url A NSURL object that provides the URL, cache policy, request type, body data or body stream, 和 so on.
/// @param dataTaskCompletionH和ler The completion h和ler to call when the load request is complete. This h和ler is executed on the delegate queue. This completion h和ler takes the following parameters:
///
/// @returns The new session data operation.
- (instancetype)initWithURL:(NSURL *)url dataTaskCompletionH和ler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionH和ler;
@end
NS_ASSUME_NONNULL_END
和
//
// DataTaskOperation.m
//
// Created by Robert Ryan on 12/12/15.
// Copyright © 2015 Robert Ryan. All rights reserved.
//
#import "DataTaskOperation.h"
@interface DataTaskOperation ()
@property (nonatomic, strong) NSURLRequest *request;
@property (nonatomic, weak) NSURLSessionTask *task;
@property (nonatomic, copy) void (^dataTaskCompletionH和ler)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error);
@end
@implementation DataTaskOperation
- (instancetype)initWithRequest:(NSURLRequest *)request dataTaskCompletionH和ler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionH和ler {
self = [super init];
if (self) {
self.request = request;
self.dataTaskCompletionH和ler = dataTaskCompletionH和ler;
}
return self;
}
- (instancetype)initWithURL:(NSURL *)url dataTaskCompletionH和ler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionH和ler {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
return [self initWithRequest:request dataTaskCompletionH和ler:dataTaskCompletionH和ler];
}
- (void)main {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:self.request completionH和ler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.dataTaskCompletionH和ler(data, response, error);
[self completeOperation];
}];
[task resume];
self.task = task;
}
- (void)completeOperation {
self.dataTaskCompletionH和ler = nil;
[super completeOperation];
}
- (void)cancel {
[self.task cancel];
[super cancel];
}
@end
哪里:
//
// AsynchronousOperation.h
//
@import Foundation;
@interface AsynchronousOperation : NSOperation
/// Complete the asynchronous operation.
///
/// This also triggers the necessary KVO to support asynchronous operations.
- (void)completeOperation;
@end
和
//
// AsynchronousOperation.m
//
#import "AsynchronousOperation.h"
@interface AsynchronousOperation ()
@property (nonatomic, getter = isFinished, readwrite) BOOL finished;
@property (nonatomic, getter = isExecuting, readwrite) BOOL executing;
@end
@implementation AsynchronousOperation
@synthesize finished = _finished;
@synthesize executing = _executing;
- (instancetype)init {
self = [super init];
if (self) {
_finished = NO;
_executing = NO;
}
return self;
}
- (void)start {
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
[self main];
}
- (void)completeOperation {
self.executing = NO;
self.finished = YES;
}
#pragma mark - NSOperation methods
- (BOOL)isAsynchronous {
return YES;
}
- (BOOL)isExecuting {
@synchronized(self) {
return _executing;
}
}
- (BOOL)isFinished {
@synchronized(self) {
return _finished;
}
}
- (void)setExecuting:(BOOL)executing {
@synchronized(self) {
if (_executing != executing) {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
}
}
- (void)setFinished:(BOOL)finished {
@synchronized(self) {
if (_finished != finished) {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
}
}
@end