得到通知时NSOperationQueue完成所有任务
-
20-08-2019 - |
题
NSOperationQueue
已经waitUntilAllOperationsAreFinished
,但我不想同步等待。我只是想隐藏进度指示器在UI时队列完成。
什么是实现这一目标的最佳方式是什么?
我无法从我的NSOperation
s发送通知,因为我不知道哪一个将是最后做的,[queue operations]
可能不是空的(或更糟 - 重新填充)。收到通知时
解决方案
使用国际志愿者组织观察你的队列operations
财产,那么你就可以判断你的队列通过检查[queue.operations count] == 0
完成。
某处你正在做国际志愿者组织在文件中声明上下文志愿就像这样(的详细信息):
static NSString *kQueueOperationsChanged = @"kQueueOperationsChanged";
当您设置您的队列中,做到这一点:
[self.queue addObserver:self forKeyPath:@"operations" options:0 context:&kQueueOperationsChanged];
然后做你的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
属性)
在你的对象之前的一些点完全deallocs(或者当它停止关心的队列状态),则需要从志愿注销是这样的:
[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),只有当他们都完成了。
谷歌搜索,我发现从苹果开发者这一声明在回答这个问题“是一个串行NSoperationQueue FIFO?” -
如果所有操作具有相同的优先级(其之后没有改变 操作添加到队列)和所有的操作总是 - 的isReady == YES,他们失去了放在操作队列的时间,然后一个串行 NSOperationQueue是FIFO。
克里斯凯恩 可可框架,苹果
在我的情况下,可以知道,当最后的操作加入到队列中。所以添加的最后一个操作后,我添加其他操作到队列中,低优先级的,它什么也不做,但发送队列已被清空的通知。鉴于苹果公司的说法,这将确保所有操作完成后,才只有一个通知发出。
如果以这样的方式不允许检测到最后一个被添加的操作,(即,非确定性),那么我想你必须去与KVO办法如上所述,通过附加保护逻辑加入到尝试检测是否可以添加另外的操作。
:)
如何添加的NSOperation是依赖于所有其他人,因此将运行多久?
一个替代方法是使用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;
}
在这个例子中“旋转器”是表示有事情发生一个UIActivityIndicatorView
。很明显,你可以改变,以适应...
关于使用志愿观察队列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];
}
使用 ReactiveObjC 我觉得这很好地工作:
// 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!");
}
}];
仅供参考,可以用GCD实现此的 dispatch_group 强>在的迅速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")
}
我使用一个类别以做到这一点。
<强> 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!)
}];
您可以创建一个新NSThread
,或者在后台执行选择,等待在那里。当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")
}
无KVO
private let queue = OperationQueue()
private func addOperations(_ operations: [Operation], completionHandler: @escaping () -> ()) {
DispatchQueue.global().async { [unowned self] in
self.queue.addOperations(operations, waitUntilFinished: true)
DispatchQueue.main.async(execute: completionHandler)
}
}