gpt4 book ai didi

ios - 带有 NSBlockOperation 和队列的 NSURLSession

转载 作者:IT老高 更新时间:2023-10-28 11:32:50 25 4
gpt4 key购买 nike

我有一个应用程序目前使用 NSURLConnection 进行绝大多数网络。我想搬到 NSURLSession 因为 Apple 告诉我这是要走的路。

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

我没有在 NSURLSession 中看到任何对同步操作的支持。我能找到的所有文章都 mock 我甚至考虑同步使用它,并且我是一个阻塞线程的可怕人。美好的。但我认为没有办法让 NSURLSessionTask 相互依赖。有没有办法做到这一点?

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

最佳答案

对同步网络请求最严厉的批评是留给那些从主队列执行的人(因为我们知道永远不应该阻塞主队列)。但是你是在你自己的后台队列上做的,它解决了同步请求中最严重的问题。但是您正在失去异步技术提供的一些出色功能(例如,如果需要,可以取消请求)。

我将在下面回答您的问题(如何使 NSURLSessionDataTask 同步运行),但我真的鼓励您接受异步模式而不是反对它们。我建议重构您的代码以使用异步模式。具体来说,如果一个任务依赖于另一个任务,只需将依赖任务的启动放在前一个任务的完成处理程序中即可。

如果您在该转换中遇到问题,请发布另一个 Stack Overflow 问题,向我们展示您的尝试,我们可以尝试帮助您。


如果您想使异步操作同步,一种常见的模式是使用分派(dispatch)信号量,以便启动异步进程的线程可以在继续之前等待来自异步操作完成 block 的信号。永远不要从主队列执行此操作,但如果您是从某个后台队列执行此操作,这可能是一种有用的模式。

您可以使用以下方法创建信号量:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

然后您可以让异步进程的完成 block 向信号量发出信号:

dispatch_semaphore_signal(semaphore);

然后您可以让代码在完成 block 之外(但仍在后台队列中,而不是主队列中)等待该信号:

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 可以优雅地处理它。


话虽如此,使用这样的 block 操作意味着操作不会响应取消事件(至少在它们运行时)。因此,我通常会使用 block 操作来避免这种信号量技术,而只是将数据任务包装在异步 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

关于ios - 带有 NSBlockOperation 和队列的 NSURLSession,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21198404/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com