当NSOperationQueue完成所有任务时获取通知
NSOperationQueue
有waitUntilAllOperationsAreFinished
,但我不想等待它同步。 我只想在队列结束时在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_group在swift中实现这个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