你问:
如何让一个操作在前一个操作完成之前不开始?
要做到这一点,理论上,您可以简单地创建一个串行队列(如果您想让 所有 操作等到前一个操作完成,这很好)。使用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 类只需在其isAsynchronous 实现中返回YES 即可将其自身指定为异步操作。因此,异步操作的抽象类实现可能如下所示:
// 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 章节,特别是标题为“为并发执行配置操作”的部分。