Kor*_*nel 89 iphone queue notifications asynchronous nsoperation
NSOperationQueue有waitUntilAllOperationsAreFinished,但我不想同步等待它.我只想在队列完成时隐藏UI中的进度指示器.
实现这一目标的最佳方法是什么?
我不能发送来自我NSOperation的通知,因为我不知道哪一个会是最后一个,并且[queue operations]在收到通知时可能不会是空的(或者更糟糕的是 - 重新填充).
Nic*_*rge 164
使用KVO观察operations队列的属性,然后通过检查确定队列是否已完成[queue.operations count] == 0.
在你正在进行KVO的文件中的某个地方,为此声明KVO的上下文(更多信息):
static NSString *kQueueOperationsChanged = @"kQueueOperationsChanged";
Run Code Online (Sandbox Code Playgroud)
设置队列时,请执行以下操作:
[self.queue addObserver:self forKeyPath:@"operations" options:0 context:&kQueueOperationsChanged];
Run Code Online (Sandbox Code Playgroud)
然后在你的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];
}
}
Run Code Online (Sandbox Code Playgroud)
(这假设您的NSOperationQueue名字属于一个属性queue)
在您的对象完全deallocs之前的某个时刻(或当它停止关注队列状态时),您需要从KVO取消注册,如下所示:
[self.queue removeObserver:self forKeyPath:@"operations" context:&kQueueOperationsChanged];
Run Code Online (Sandbox Code Playgroud)
附录:iOS 4.0有一个NSOperationQueue.operationCount属性,根据文档符合KVO.这个答案仍然适用于iOS 4.0,因此它仍然有助于向后兼容.
sof*_*ved 20
如果您期望(或希望)符合此行为的内容:
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>
Run Code Online (Sandbox Code Playgroud)
您应该知道,如果向队列添加了许多"短"操作,您可能会看到此行为(因为操作是作为添加到队列的一部分而启动的):
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>
Run Code Online (Sandbox Code Playgroud)
在我的项目中,我需要知道在将大量操作添加到串行NSOperationQueue(即maxConcurrentOperationCount = 1)之后并且仅在它们全部完成时才完成最后一次操作.
谷歌搜索我发现Apple开发人员回答"是一个连续的NSoperationQueue FIFO?"这个问题的声明. -
如果所有操作具有相同的优先级(在将操作添加到队列后未更改)并且所有操作始终为 - isReady == YES,那么它们被放入操作队列时,则串行NSOperationQueue为FIFO.
Chris Kane Cocoa Frameworks,Apple
在我的例子中,可以知道最后一个操作何时被添加到队列中.因此,在添加最后一个操作之后,我将另一个操作添加到队列中,优先级较低,除了发送队列已清空的通知之外什么都不做.鉴于Apple的声明,这确保了仅在所有操作完成后才发送一个通知.
如果以不允许检测最后一个的方式添加操作(即,非确定性),那么我认为你必须采用上面提到的KVO方法,添加额外的保护逻辑以试图进一步检测可以添加操作.
:)
小智 12
一种选择是使用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
});
Run Code Online (Sandbox Code Playgroud)
As of iOS 13.0, the operationCount and operation properties are deprecated. It's just as simple to keep track of the number of operations in your queue yourself and fire off a Notification when they've all completed. This example works with an asynchronous subclassing of Operation too.
class MyOperationQueue: OperationQueue {
public var numberOfOperations: Int = 0 {
didSet {
if numberOfOperations == 0 {
print("All operations completed.")
NotificationCenter.default.post(name: .init("OperationsCompleted"), object: nil)
}
}
}
public var isEmpty: Bool {
return numberOfOperations == 0
}
override func addOperation(_ op: Operation) {
super.addOperation(op)
numberOfOperations += 1
}
override func addOperations(_ ops: [Operation], waitUntilFinished wait: Bool) {
super.addOperations(ops, waitUntilFinished: wait)
numberOfOperations += ops.count
}
public func decrementOperationCount() {
numberOfOperations -= 1
}
}
Run Code Online (Sandbox Code Playgroud)
Below is a subclass of Operation for easy asynchronous operations
class AsyncOperation: Operation {
let queue: MyOperationQueue
enum State: String {
case Ready, Executing, Finished
fileprivate var keyPath: String {
return "is" + rawValue
}
}
var state = State.Ready {
willSet {
willChangeValue(forKey: newValue.keyPath)
willChangeValue(forKey: state.keyPath)
}
didSet {
didChangeValue(forKey: oldValue.keyPath)
didChangeValue(forKey: state.keyPath)
if state == .Finished {
queue.decrementOperationCount()
}
}
}
override var isReady: Bool {
return super.isReady && state == .Ready
}
override var isExecuting: Bool {
return state == .Executing
}
override var isFinished: Bool {
return state == .Finished
}
override var isAsynchronous: Bool {
return true
}
public init(queue: MyOperationQueue) {
self.queue = queue
super.init()
}
override func start() {
if isCancelled {
state = .Finished
return
}
main()
state = .Executing
}
override func cancel() {
state = .Finished
}
override func main() {
fatalError("Subclasses must override main without calling super.")
}
Run Code Online (Sandbox Code Playgroud)
}
我就是这样做的.
设置队列,并注册operations属性中的更改:
myQueue = [[NSOperationQueue alloc] init];
[myQueue addObserver: self forKeyPath: @"operations" options: NSKeyValueObservingOptionNew context: NULL];
Run Code Online (Sandbox Code Playgroud)
......和观察者(在这种情况下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;
}
Run Code Online (Sandbox Code Playgroud)
在这个例子中,"微调器" UIActivityIndicatorView表示发生了某些事情.显然你可以换到适合......
| 归档时间: |
|
| 查看次数: |
45753 次 |
| 最近记录: |