我有一个应用程序,目前其绝大多数网络使用NSURLConnection.我想转到NSURLSession,因为苹果告诉我,这是一条路要走.

我的应用程序只是通过+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error类方法使用了NSURLConnection的同步版本.我在NSOperationQueue上运行的NSBlockOperation中执行此操作,因此我不会不必要地阻塞主队列.这样做的最大好处是,我可以使操作相互依赖.例如,我可以让请求数据的任务依赖于登录任务的完成.

我还没有看到任何对NSURLSession秒内同步操作的支持.我能找到的只是一些文章,嘲笑我甚至想过同步使用它,还说我是一个屏蔽线程的可怕的人.好的但我看不到让NSURLSessionTask人相互依赖的方法.有办法吗?

或者有没有描述过我将如何以不同的方式做这样的事情?

推荐答案

对同步网络请求最严厉的批评是留给那些从主队列执行同步请求的人的(因为我们知道,永远不应该阻塞主队列).但您是在自己的后台队列上完成的,它解决了同步请求中最严重的问题.但是你正在失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

Objective-c相关问答推荐

如何从 Cocoa 的 NSDictionary 中获取特定键的值?

Xcode 的任何 ReSharper 类似功能?

NSPredicate 与整数比较

从 NSError 返回 NSString

指向方法返回类型的Expected a type错误

适用于 ios 的 360° 全景库

如何在另一个视图中获取视图的框架?

iOS 7 - viewDidLoad 和 viewDidAppear 之间的区别

从我在 iPhone 上的应用程序调用官方 *Settings* 应用程序

如何从 AVPlayer(不是 AVAudioPlayer)获取持续时间?

Apple 的 API 中的k前缀表示什么?

如何删除手势识别器

使用 OpenCV 在 iOS 中进行透视变换 + 裁剪

主队列上的 dispatch_sync 与 dispatch_async

UIWebView didFinishLoading 多次触发

如何在嵌入 UIViewController 的 UITableView 中直观地创建和使用静态单元格

立即检测 iOS 方向变化

UIScrollView - 显示滚动条

@dynamic 在 Objective-C 中做了什么?

UICollectionView 断言失败