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

发布于 2024-07-25 08:33:05 字数 269 浏览 4 评论 0原文

NSOperationQueuewaitUntilAllOperationsAreFinished,但我不想同步等待它。 我只想在队列完成时隐藏用户界面中的进度指示器。

实现这一目标的最佳方法是什么?

我无法从我的 NSOperation 发送通知,因为我不知道哪一个将是最后一个,并且 [queue actions] 可能还不为空(或者更糟 - 重新填充)收到通知时。

NSOperationQueue has waitUntilAllOperationsAreFinished, but I don't want to wait synchronously for it. I just want to hide progress indicator in UI when queue finishes.

What's the best way to accomplish this?

I can't send notifications from my NSOperations, because I don't know which one is going to be last, and [queue operations] might not be empty yet (or worse - repopulated) when notification is received.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

绳情 2024-08-01 08:33:05

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

在您执行 KVO 的文件中的某个位置,为 KVO 声明一个上下文,如下所示 (更多信息):

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 的属性中)在

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

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

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

Use KVO to observe the operations property of your queue, then you can tell if your queue has completed by checking for [queue.operations count] == 0.

Somewhere in the file you're doing the KVO in, declare a context for KVO like this (more info):

static NSString *kQueueOperationsChanged = @"kQueueOperationsChanged";

When you setup your queue, do this:

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

Then do this in your 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];
    }
}

(This is assuming that your NSOperationQueue is in a property named queue)

At some point before your object fully deallocs (or when it stops caring about the queue state), you'll need to unregister from KVO like this:

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

Addendum: iOS 4.0 has an NSOperationQueue.operationCount property, which according to the docs is KVO compliant. This answer will still work in iOS 4.0 however, so it's still useful for backwards compatibility.

第几種人 2024-08-01 08:33:05

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

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 是先进先出的。

克里斯·凯恩
可可框架,苹果

在我的例子中,可以知道最后一个操作何时添加到队列中。 因此,在添加最后一个操作后,我向队列中添加另一个优先级较低的操作,该操作除了发送队列已清空的通知之外什么也不做。 根据苹果的声明,这可以确保仅在所有操作完成后才发送单个通知。

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

:)

If you are expecting (or desiring) something that matches this behavior:

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>

You should be aware that if a number of "short" operations are being added to a queue you may see this behavior instead (because operations are started as part of being added to the queue):

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>

In my project I needed to know when the last operation completed, after a large number of operations had been added to a serial NSOperationQueue (ie, maxConcurrentOperationCount=1) and only when they had all completed.

Googling I found this statement from an Apple developer in response to the question "is a serial NSoperationQueue FIFO?" --

If all operations have the same priority (which is not changed after
the operation is added to a queue) and all operations are always -
isReady==YES by the time they get put in the operation queue, then a serial
NSOperationQueue is FIFO.

Chris Kane
Cocoa Frameworks, Apple

In my case it is possible to know when the last operation was added to the queue. So after the last operation is added, I add another operation to the queue, of lower priority, which does nothing but send the notification that the queue had been emptied. Given Apple's statement, this ensures that only a single notice is sent only after all operations have been completed.

If operations are being added in a manner which doesn't allow detecting the last one, (ie, non-deterministic) then I think you have to go with the KVO approaches mentioned above, with additional guard logic added to try to detect if further operations may be added.

:)

芸娘子的小脾气 2024-08-01 08:33:05

添加一个依赖于所有其他操作的 NSOperation 以便它最后运行怎么样?

How about adding an NSOperation that is dependent on all others so it will run last?

走野 2024-08-01 08:33:05

一种替代方法是使用 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
});

One alternative is to use GCD. Refer to this as reference.

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
});
深者入戏 2024-08-01 08:33:05

iOS 13.0 开始,operationCountoperation 属性已弃用。 您可以自己跟踪队列中的操作数量,并在所有操作完成后发出通知,这也同样简单。 此示例也适用于 Operation 的异步子类化。

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
    }
}

下面是用于简单异步操作的Operation的子类

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.")
}

}

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
    }
}

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.")
}

}

稳稳的幸福 2024-08-01 08:33:05

我就是这样做的。

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

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”是一个 UIActivityIndi​​catorView 表明某事正在发生。 显然你可以改变以适应...

This is how I do it.

Set up the queue, and register for changes in the operations property:

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

...and the observer (in this case self) implements:

- (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;
}

In this example "spinner" is a UIActivityIndicatorView showing that something is happening. Obviously you can change to suit...

伤感在游骋 2024-08-01 08:33:05

我正在使用一个类别来执行此操作。

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

I'm using a category to do this.

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

Usage:

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!)
}];

Source: https://gist.github.com/artemstepanenko/7620471

躲猫猫 2024-08-01 08:33:05

使用 KVO 来观察队列的 operationCount 属性怎么样? 然后,当队列变空时,以及当队列不再空时,您会听到它。 处理进度指示器可能就像执行以下操作一样简单:

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

What about using KVO to observe the operationCount property of the queue? Then you'd hear about it when the queue went to empty, and also when it stopped being empty. Dealing with the progress indicator might be as simple as just doing something like:

[indicator setHidden:([queue operationCount]==0)]
揽月 2024-08-01 08:33:05

添加最后一个操作,例如:

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]; 

}

Add the last operation like:

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

So:

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

}
歌枕肩 2024-08-01 08:33:05

使用 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!");
    }
}];

With ReactiveObjC I find this works nicely:

// 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!");
    }
}];
转身以后 2024-08-01 08:33:05

仅供参考,您可以使用 swift 3 中的 GCD dispatch_group 来实现此目的。 所有任务完成后您都会收到通知。

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")
}

FYI,You can achieve this with GCD dispatch_group in swift 3. You can get notified when all tasks are finished.

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")
}
三生一梦 2024-08-01 08:33:05

let queue = OperationQueue()
queue.underlyingQueue = .global(qos: .background)
queue.progress.totalUnitCount = 3
queue.isSuspended = true

queue.addOperation(blockOperation1)
queue.addOperation(blockOperation2)
queue.addOperation(blockOperation3)

/// add at end if any operation is added after addBarrierBlock then that operation will wait unit BarrierBlock is finished

queue.addBarrierBlock {
    print("All operations are finished \(queue.progress.fractionCompleted) - \(queue.progress.completedUnitCount)" )
}


queue.isSuspended = false

let queue = OperationQueue()
queue.underlyingQueue = .global(qos: .background)
queue.progress.totalUnitCount = 3
queue.isSuspended = true

queue.addOperation(blockOperation1)
queue.addOperation(blockOperation2)
queue.addOperation(blockOperation3)

/// add at end if any operation is added after addBarrierBlock then that operation will wait unit BarrierBlock is finished

queue.addBarrierBlock {
    print("All operations are finished \(queue.progress.fractionCompleted) - \(queue.progress.completedUnitCount)" )
}


queue.isSuspended = false
浅沫记忆 2024-08-01 08:33:05

从 iOS 13 开始,您可以使用 https://developer.apple.com/documentation /foundation/operationqueue/3172534-addbarrierblock

当队列完成所有排队操作时调用一个块,并阻止后续操作开始,直到该块完成为止。

queue.addBarrierBlock {
    
}

From iOS 13, you can use https://developer.apple.com/documentation/foundation/operationqueue/3172534-addbarrierblock

Invokes a block when the queue finishes all enqueued operations, and prevents subsequent operations from starting until the block has completed.

queue.addBarrierBlock {
    
}
叶落知秋 2024-08-01 08:33:05

您可以创建一个新的 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"];
}

You can create a new NSThread, or execute a selector in background, and wait in there. When the NSOperationQueue finishes, you can send a notification of your own.

I'm thinking on something like:

- (void)someMethod {
    // Queue everything in your operationQueue (instance variable)
    [self performSelectorInBackground:@selector(waitForQueue)];
    // Continue as usual
}

...

- (void)waitForQueue {
    [operationQueue waitUntilAllOperationsAreFinished];
    [[NSNotificationCenter defaultCenter] postNotification:@"queueFinished"];
}
爱殇璃 2024-08-01 08:33:05

如果您使用此 Operation 作为基类,您可以传递 whenEmpty {}< /code> 块到 OperationQueue

let queue = OOperationQueue()
queue.addOperation(op)
queue.addOperation(delayOp)

queue.addExecution { finished in
    delay(0.5) { finished() }
}

queue.whenEmpty = {
    print("all operations finished")
}

If you use this Operation as your base class, you could pass whenEmpty {} block to the OperationQueue:

let queue = OOperationQueue()
queue.addOperation(op)
queue.addOperation(delayOp)

queue.addExecution { finished in
    delay(0.5) { finished() }
}

queue.whenEmpty = {
    print("all operations finished")
}
谈情不如逗狗 2024-08-01 08:33:05

没有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)
    }
}

Without 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)
    }
}
热血少△年 2024-08-01 08:33:05

如果你来这里寻找组合的解决方案 - 我最终只是听我自己的状态对象。

@Published var state: OperationState = .ready
var sub: Any?

sub = self.$state.sink(receiveValue: { (state) in
 print("state updated: \(state)")
})

If you got here looking for a solution with combine - I ended up just listening to my own state object.

@Published var state: OperationState = .ready
var sub: Any?

sub = self.$state.sink(receiveValue: { (state) in
 print("state updated: \(state)")
})
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文