ios - 保持 NSOperationQueue 直到上一个操作完成

标签 ios objective-c nsoperationqueue

我想执行一些操作,并且只需要在上一个操作完成后才开始下一个操作。我添加的操作将向服务器发送异步调用并接收数据。我只想在第一次调用服务器完成从服务器接收数据后才开始下一个操作。如何做到这一点?

{.... 
     PhotoDownloader *pd = [[PhotoDownloader alloc] init];
     [GetGlobalOperationQueue addOperation:pd]; 
}

在 PhotoDownloader 中,我将分配所需的参数并调用处理所有请求的全局函数

[GlobalCommunicationUtil sendServerReq:reqObj withResponseHandler:self];

在 sendServerReq 方法中,我将构造 URL 请求并将其发送到服务器,此调用是“sendAsynchronousRequest”调用。 PhotoDownloader 将具有 CommunicationUtil 的委托(delegate)方法。

最佳答案

这个问题有两个部分:

  1. 你问:

    How do I make one operation not start until the previous operation finishes?

    为此,从理论上讲,您可以简单地创建一个串行队列(如果您想让所有 操作等到前一个完成,这很好)。使用 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];
    
  2. 你问:

    How do I ensure that an operation will not complete until the asynchronous network request it issued has finished as well?

    同样,这里有不同的方法。有时您可以利用信号量使异步进程同步。但是,更好的方法是使用并发 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
    

    现在,在这个例子中,我使用这些异步网络请求的基于 block 的实现,但这个想法在基于委托(delegate)的连接/ session 中也同样有效。 (唯一的麻烦是 NSURLSession 将其任务相关的委托(delegate)方法指定为 session 的一部分,而不是网络任务。)

    很明显,您自己的 NetworkOperation 类的实现可能大相径庭(使用委托(delegate)模式或完成 block 模式等),但希望这能说明并发操作的想法。有关详细信息,请参阅 Operation Queues 并发编程指南的一章,特别是标题为“为并发执行配置操作”的部分。

关于ios - 保持 NSOperationQueue 直到上一个操作完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24933826/

相关文章:

iphone - iOS 上 SQLite 文件的统一类型标识符 (UTI) 是什么?

iphone - 如何取消 NSOperationQueue

ios - 将主队列设置为 NSOperationQueue 的底层队列

objective-c - 为什么 NSWindow 不恢复其状态(在 Lion 中使用 Resume)?

ios - textview 文本到数组中

objective-c - IOS 从一个 View 访问另一个 View

ios - NSOperation - setMaxConcurrentOperationCount

ios - 导入自身时生成错误的 Module-Swift.h header

iphone - 带虚线的 UIView

ios - 在 Swift 3 中的文本中的某个字符串之前放置一个字符串