当NSOperationQueue完成所有任务时获取通知

NSOperationQueuewaitUntilAllOperationsAreFinished ,但我不想等待它同步。 我只想在队列结束时在UI中隐藏进度指示器。

什么是完成这个最好的方法?

我不能发送来自我的NSOperation的通知,因为我不知道哪一个会是最后一个,并且当收到通知时[queue operations]可能还没有空(或更糟 – 重新填充)。

使用KVO观察队列的operations属性,然后通过检查[queue.operations count] == 0来判断队列是否已经完成。

在文件的某处你正在做KVO,像这样声明KVO的上下文( 更多信息 ):

 static NSString *kQueueOperationsChanged = @"kQueueOperationsChanged"; 

当您设置您的队列时,请执行以下操作:

 [self.queue addObserver:self forKeyPath:@"operations" options:0 context:&kQueueOperationsChanged]; 

然后在您的observeValueForKeyPath执行此observeValueForKeyPath

 - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == self.queue && [keyPath isEqualToString:@"operations"] && context == &kQueueOperationsChanged) { if ([self.queue.operations count] == 0) { // Do something here when your queue has completed NSLog(@"queue has completed"); } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } 

(这是假设您的NSOperationQueue是在一个名为queue的属性)

在你的对象完全释放之前(或者当它停止关注队列状态)之前的某个时刻,你需要像这样从KVO注销:

 [self.queue removeObserver:self forKeyPath:@"operations" context:&kQueueOperationsChanged]; 

附录:iOS 4.0有一个NSOperationQueue.operationCount属性,根据文档是KVO兼容的。 这个答案仍然可以在iOS 4.0中使用,所以它对于向后兼容性仍然有用。

如果您期望(或期望)与此行为相匹配的内容:

 t=0 add an operation to the queue. queueucount increments to 1 t=1 add an operation to the queue. queueucount increments to 2 t=2 add an operation to the queue. queueucount increments to 3 t=3 operation completes, queuecount decrements to 2 t=4 operation completes, queuecount decrements to 1 t=5 operation completes, queuecount decrements to 0 <your program gets notified that all operations are completed> 

您应该知道,如果将一些“短”操作添加到队列中,您可能会看到此行为(因为操作是作为被添加到队列中的一部分而开始的):

 t=0 add an operation to the queue. queuecount == 1 t=1 operation completes, queuecount decrements to 0 <your program gets notified that all operations are completed> t=2 add an operation to the queue. queuecount == 1 t=3 operation completes, queuecount decrements to 0 <your program gets notified that all operations are completed> t=4 add an operation to the queue. queuecount == 1 t=5 operation completes, queuecount decrements to 0 <your program gets notified that all operations are completed> 

在我的项目中,我需要知道什么时候最后一个操作完成后,大量的操作被添加到串行NSOperationQueue(即,maxConcurrentOperationCount = 1),并且只有当他们全部完成。

谷歌search我发现这个从苹果开发人员的声明,以回应“是一个串行的NSoperationQueue FIFO?”的问题。 –

如果所有的操作具有相同的优先级(在操作被添加到队列后没有改变),并且所有的操作在被放入操作队列时都是 – isReady == YES,那么串行的NSOperationQueue就是FIFO。

克里斯凯恩cocoa框架,苹果

在我的情况下,可以知道最后一个操作何时被添加到队列中。 因此,在添加最后一个操作之后,我将另一个操作添加到优先级较低的队列中,该队列除了发送队列已被清空的通知之外什么也不做。 鉴于苹果的声明,这确保只有一个通知是在所有操作完成后才发送的。

如果以不允许检测最后一个操作的方式添加操作(即,非确定性),那么我认为您必须采用上述的KVO方法,并添加额外的保护逻辑以尝试检测是否进一步可能会添加操作。

🙂

如何添加一个依赖于所有其他的NSOperation,以便最后运行?

一种select是使用GCD。 参考这个作为参考。

 dispatch_queue_t queue = dispatch_get_global_queue(0,0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group,queue,^{ NSLog(@"Block 1"); //run first NSOperation here }); dispatch_group_async(group,queue,^{ NSLog(@"Block 2"); //run second NSOperation here }); //or from for loop for (NSOperation *operation in operations) { dispatch_group_async(group,queue,^{ [operation start]; }); } dispatch_group_notify(group,queue,^{ NSLog(@"Final block"); //hide progress indicator here }); 

这是我如何做到的。

设置队列,并在操作属性中注册更改:

 myQueue = [[NSOperationQueue alloc] init]; [myQueue addObserver: self forKeyPath: @"operations" options: NSKeyValueObservingOptionNew context: NULL]; 

…和观察者(在这种情况下是self )实现:

 - (void) observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context { if ( object == myQueue && [@"operations" isEqual: keyPath] ) { NSArray *operations = [change objectForKey:NSKeyValueChangeNewKey]; if ( [self hasActiveOperations: operations] ) { [spinner startAnimating]; } else { [spinner stopAnimating]; } } } - (BOOL) hasActiveOperations:(NSArray *) operations { for ( id operation in operations ) { if ( [operation isExecuting] && ! [operation isCancelled] ) { return YES; } } return NO; } 

在这个例子中,“spinner”是一个UIActivityIndicatorView显示正在发生的事情。 显然你可以改变以适应…

与ReactiveCocoa我觉得这个作品很好:

 // skip 1 time here to ignore the very first call which occurs upon initialization of the RAC block [[RACObserve(self.operationQueue, operationCount) skip:1] subscribeNext:^(NSNumber *operationCount) { if ([operationCount integerValue] == 0) { // operations are done processing NSLog(@"Finished!"); } }]; 

使用KVO观察队列的operationCount属性怎么样? 然后,当队列变空时,以及停止变空时,你会听到这个消息。 处理进度指标可能就像做下面的事情一样简单:

 [indicator setHidden:([queue operationCount]==0)] 

添加最后一个操作,如:

 NSInvocationOperation *callbackOperation = [[NSInvocationOperation alloc] initWithTarget:object selector:selector object:nil]; 

所以:

 - (void)method:(id)object withSelector:(SEL)selector{ NSInvocationOperation *callbackOperation = [[NSInvocationOperation alloc] initWithTarget:object selector:selector object:nil]; [callbackOperation addDependency: ...]; [operationQueue addOperation:callbackOperation]; } 

FYI,你可以用GCD dispatch_groupswift中实现这个3 。 所有任务完成后,您都可以收到通知。

 let group = DispatchGroup() group.enter() run(after: 6) { print(" 6 seconds") group.leave() } group.enter() run(after: 4) { print(" 4 seconds") group.leave() } group.enter() run(after: 2) { print(" 2 seconds") group.leave() } group.enter() run(after: 1) { print(" 1 second") group.leave() } group.notify(queue: DispatchQueue.global(qos: .background)) { print("All async calls completed") } 

你可以创build一个新的NSThread ,或者在后台执行一个select器,并在那里等待。 当NSOperationQueue完成后,您可以发送自己的通知。

我正在考虑这样的事情:

 - (void)someMethod { // Queue everything in your operationQueue (instance variable) [self performSelectorInBackground:@selector(waitForQueue)]; // Continue as usual } ... - (void)waitForQueue { [operationQueue waitUntilAllOperationsAreFinished]; [[NSNotificationCenter defaultCenter] postNotification:@"queueFinished"]; } 

如果你使用这个操作作为你的基类,你可以传递whenEmpty {}块给OperationQueue :

 let queue = OOperationQueue() queue.addOperation(op) queue.addOperation(delayOp) queue.addExecution { finished in delay(0.5) { finished() } } queue.whenEmpty = { print("all operations finished") } 

我正在使用一个类别来做到这一点。

NSOperationQueue + Completion.h

 // // NSOperationQueue+Completion.h // QueueTest // // Created by Artem Stepanenko on 23.11.13. // Copyright (c) 2013 Artem Stepanenko. All rights reserved. // typedef void (^NSOperationQueueCompletion) (void); @interface NSOperationQueue (Completion) /** * Remarks: * * 1. Invokes completion handler just a single time when previously added operations are finished. * 2. Completion handler is called in a main thread. */ - (void)setCompletion:(NSOperationQueueCompletion)completion; @end 

NSOperationQueue + Completion.m

 // // NSOperationQueue+Completion.m // QueueTest // // Created by Artem Stepanenko on 23.11.13. // Copyright (c) 2013 Artem Stepanenko. All rights reserved. // #import "NSOperationQueue+Completion.h" @implementation NSOperationQueue (Completion) - (void)setCompletion:(NSOperationQueueCompletion)completion { NSOperationQueueCompletion copiedCompletion = [completion copy]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self waitUntilAllOperationsAreFinished]; dispatch_async(dispatch_get_main_queue(), ^{ copiedCompletion(); }); }); } @end 

用法

 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ // ... }]; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ // ... }]; [operation2 addDependency:operation1]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[operation1, operation2] waitUntilFinished:YES]; [queue setCompletion:^{ // handle operation queue's completion here (launched in main thread!) }]; 

来源: https : //gist.github.com/artemstepanenko/7620471