Самая резкая критика синхронных сетевых запросов предназначена для тех, кто делает это из основной очереди (так как мы знаем, что нельзя блокировать основную очередь). Но вы делаете это в своей собственной фоновой очереди, которая решает самую вопиющую проблему с синхронными запросами. Но вы теряете некоторые замечательные функции, которые предоставляют асинхронные методы (например, отмену запросов, если это необходимо).
Я отвечу на ваш вопрос (как заставить NSURLSessionDataTask
вести себя синхронно) ниже, но я действительно призываю вас принять асинхронные шаблоны, а не бороться с ними. Я бы предложил рефакторинг вашего кода для использования асинхронных шаблонов. В частности, если одна задача зависит от другой, просто поместите инициацию зависимой задачи в обработчик завершения предыдущей задачи.
Если у вас возникли проблемы с этим преобразованием, опубликуйте еще один вопрос о переполнении стека, показав нам, что вы пробовали, и мы можем попытаться вам помочь.
Если вы хотите сделать асинхронную операцию синхронной, распространенным шаблоном является использование семафора отправки, чтобы ваш поток, инициировавший асинхронный процесс, мог дождаться сигнала от блока завершения асинхронной операции, прежде чем продолжить. Никогда не делайте этого из основной очереди, но если вы делаете это из какой-то фоновой очереди, это может быть полезным шаблоном.
Вы можете создать семафор с помощью:
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 completionHandler:^(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, and calls a handler upon completion.
///
/// @param request A NSURLRequest object that provides the URL, cache policy, request type, body data or body stream, and so on.
/// @param dataTaskCompletionHandler The completion handler to call when the load request is complete. This handler is executed on the delegate queue. This completion handler takes the following parameters:
///
/// @returns The new session data operation.
- (instancetype)initWithRequest:(NSURLRequest *)request dataTaskCompletionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionHandler;
/// Creates a operation that retrieves the contents of a URL based on the specified URL request object, and calls a handler upon completion.
///
/// @param url A NSURL object that provides the URL, cache policy, request type, body data or body stream, and so on.
/// @param dataTaskCompletionHandler The completion handler to call when the load request is complete. This handler is executed on the delegate queue. This completion handler takes the following parameters:
///
/// @returns The new session data operation.
- (instancetype)initWithURL:(NSURL *)url dataTaskCompletionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionHandler;
@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 (^dataTaskCompletionHandler)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error);
@end
@implementation DataTaskOperation
- (instancetype)initWithRequest:(NSURLRequest *)request dataTaskCompletionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionHandler {
self = [super init];
if (self) {
self.request = request;
self.dataTaskCompletionHandler = dataTaskCompletionHandler;
}
return self;
}
- (instancetype)initWithURL:(NSURL *)url dataTaskCompletionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))dataTaskCompletionHandler {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
return [self initWithRequest:request dataTaskCompletionHandler:dataTaskCompletionHandler];
}
- (void)main {
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:self.request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.dataTaskCompletionHandler(data, response, error);
[self completeOperation];
}];
[task resume];
self.task = task;
}
- (void)completeOperation {
self.dataTaskCompletionHandler = 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
person
Rob
schedule
18.01.2014
NSURLConnection sendSynchronousRequest:
. - person aroth   schedule 01.04.2016