我想执行一些操作,只有在完成上一个操作后才需要启动下一个操作。我正在添加的操作将向服务器发送异步调用并接收数据。我想在第一次调用服务器完成从服务器接收数据后才开始下一个操作。怎么做?
{....
PhotoDownloader *pd = [[PhotoDownloader alloc] init];
[GetGlobalOperationQueue addOperation:pd];
}
在PhotoDownloader中,我将分配所需的参数并调用处理所有请求的Global函数
[GlobalCommunicationUtil sendServerReq:reqObj withResponseHandler:self];
在sendServerReq方法中,我将构造URL请求并将其发送到服务器,此调用是“sendAsynchronousRequest”调用。 PhotoDownloader将具有CommunicationUtil的委托方法。
答案 0 :(得分:19)
这个问题分为两部分:
你问:
如果在上一个操作完成之前不能启动一个操作?
要做到这一点,理论上,您可以简单地创建一个串行队列(如果您想让所有操作等到前一个操作完成,那就没问题)。使用NSOperationQueue
,只需将maxConcurrentOperationCount
设置为1
即可实现此目的。
或者,更灵活一点,您可以在需要依赖关系的操作之间建立依赖关系,但另外还要享受并发性。例如,如果您希望根据完成第三个网络请求制作两个网络请求,则可以执行以下操作:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4; // generally with network requests, you don't want to exceed 4 or 5 concurrent operations;
// it doesn't matter too much here, since there are only 3 operations, but don't
// try to run more than 4 or 5 network requests at the same time
NSOperation *operation1 = [[NetworkOperation alloc] initWithRequest:request1 completionHandler:^(NSData *data, NSError *error) {
[self doSomethingWithData:data fromRequest:request1 error:error];
}];
NSOperation *operation2 = [[NetworkOperation alloc] initWithRequest:request2 completionHandler:^(NSData *data, NSError *error) {
[self doSomethingWithData:data fromRequest:request2 error:error];
}];
NSOperation *operation3 = [[NetworkOperation alloc] initWithRequest:request3 completionHandler:^(NSData *data, NSError *error) {
[self doSomethingWithData:data fromRequest:request3 error:error];
}];
[operation2 addDependency:operation1]; // don't start operation2 or 3 until operation1 is done
[operation3 addDependency:operation1];
[queue addOperation:operation1]; // now add all three to the queue
[queue addOperation:operation2];
[queue addOperation:operation3];
你问:
如果在发出的异步网络请求完成之后,如何确保操作无法完成?
同样,这里有不同的方法。有时您可以利用信号量来使异步进程同步。但是,更好的是使用并发NSOperation
子类。
"异步" NSOperation
只是在发出isFinished
通知之前无法完成的(从而允许它启动的任何异步任务完成)。并且NSOperation
类仅通过在YES
实现中返回isAsynchronous
将其自身指定为异步操作。因此,异步操作的抽象类实现可能如下所示:
// AsynchronousOperation.h
@import Foundation;
@interface AsynchronousOperation : NSOperation
/**
Complete the asynchronous operation.
If you create an asynchronous operation, you _must_ call this for all paths of execution
or else the operation will not terminate (and dependent operations and/or available
concurrent threads for the operation queue (`maxConcurrentOperationCount`) will be blocked.
*/
- (void)completeOperation;
@end
和
//
// AsynchronousOperation.m
//
#import "AsynchronousOperation.h"
@interface AsynchronousOperation ()
@property (getter = isFinished, readwrite) BOOL finished;
@property (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) {
if (!self.isFinished) self.finished = YES;
return;
}
self.executing = YES;
[self main];
}
- (void)completeOperation {
if (self.isExecuting) self.executing = NO;
if (!self.isFinished) 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 {
[self willChangeValueForKey:@"isExecuting"];
@synchronized(self) { _executing = executing; }
[self didChangeValueForKey:@"isExecuting"];
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
@synchronized(self) { _finished = finished; }
[self didChangeValueForKey:@"isFinished"];
}
@end
现在我们有了这个抽象的异步NSOperation
子类,我们可以在具体的NetworkOperation
类中使用它:
#import "AsynchronousOperation.h"
NS_ASSUME_NONNULL_BEGIN
typedef void(^NetworkOperationCompletionBlock)(NSData * _Nullable data, NSError * _Nullable error);
@interface NetworkOperation : AsynchronousOperation
@property (nullable, nonatomic, copy) NetworkOperationCompletionBlock networkOperationCompletionBlock;
@property (nonatomic, copy) NSURLRequest *request;
- (instancetype)initWithRequest:(NSURLRequest *)request completionHandler:(NetworkOperationCompletionBlock)completionHandler;
@end
NS_ASSUME_NONNULL_END
和
// NetworkOperation.m
#import "NetworkOperation.h"
@interface NetworkOperation ()
@property (nonatomic, weak) NSURLSessionTask *task;
@end
@implementation NetworkOperation
- (instancetype)initWithRequest:(NSURLRequest *)request completionHandler:(NetworkOperationCompletionBlock)completionHandler {
self = [self init];
if (self) {
self.request = request;
self.networkOperationCompletionBlock = completionHandler;
}
return self;
}
- (void)main {
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionTask *task = [session dataTaskWithRequest:self.request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (self.networkOperationCompletionBlock) {
self.networkOperationCompletionBlock(data, error);
self.networkOperationCompletionBlock = nil;
}
[self completeOperation];
}];
[task resume];
self.task = task;
}
- (void)cancel {
[super cancel];
[self.task cancel];
}
@end
现在,在这个例子中,我使用了基于块的这些异步网络请求的实现,但这个想法在基于委托的连接/会话中同样有效。 (唯一的麻烦是NSURLSession
指定其与任务相关的委托方法是会话的一部分,而不是网络任务。)
显然,您自己的NetworkOperation
类的实现可能会有很大不同(使用委托模式或完成块模式等),但希望这说明了并发操作的想法。有关详细信息,请参阅并发编程指南的Operation Queues章节,特别是标题为"配置并发执行操作的部分"
答案 1 :(得分:1)
异步操作的快速版本(不是很明显):
final class NetworkOperation: Operation {
lazy var session: NSURLSession = {
return NSURLSession.sharedSession()
}()
private var _finished = false {
willSet {
willChangeValue(forKey: "isFinished")
}
didSet {
didChangeValue(forKey: "isFinished")
}
}
private var _executing = false {
willSet {
willChangeValue(forKey: "isExecuting")
}
didSet {
didChangeValue(forKey: "isExecuting")
}
}
override var isAsynchronous: Bool {
return true
}
override var isFinished: Bool {
return _finished
}
override var isExecuting: Bool {
return _executing
}
override func start() {
_executing = true
execute()
}
func execute() {
task = session.downloadTaskWithURL(NSURL(string: "yourURL")!) {
(url, response, error) in
if error == nil {
// Notify the response by means of a closure or what you prefer
// Remember to run in the main thread since NSURLSession runs its
// task on background by default
} else {
// Notify the failure by means of a closure or what you prefer
// Remember to run in the main thread since NSURLSession runs its
// task on background by default
}
// Remember to tell the operation queue that the execution has completed
self.finish()
}
}
func finish() {
//Async task complete and hence the operation is complete
_executing = false
_finished = true
}
}
序列化操作:
let operationQueue = OperationQueue()
let operation1 = NetworkOperation()
let operation2 = NetworkOperation()
operation2.addDependency(operation1)
operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)
答案 2 :(得分:0)
使用NSOperationQueue是否具有管理能力?
使用串行队列
可以很容易地实现此行为假设您有一个用于管理操作的类,您将在init方法中使用
创建一个串行调度队列queue = dispatch_queue_create("com.example.MyQueue", NULL);
你会有一个方法来排队请求,像这样
- (void) enqueueRequest:(NSURL *)requestURL
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(queue, ^{ /* get data from requestURL */ }) });
}
这样,一次只有一个请求处于活动状态,即使每个请求都将在一个单独的后台线程中执行,并且几个请求将被排队,直到活动请求完成。