NSOperationQueue에서 모든 작업이 완료되면 알림 받기
NSOperationQueue
가지다waitUntilAllOperationsAreFinished
싶지 큐가 끝나면 UI에 진행 표시기를 숨기고 싶습니다.
이것을 달성하는 가장 좋은 방법은 무엇입니까?
수 NSOperation
어떤 s, 입니다.[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
:
- (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.0NSOperationQueue.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>
프로젝트에서 저는 마지막 작업이 언제 완료되었는지, 수많은 작업이 직렬 NSO OperationQueue에 추가된 후(즉, maxConcurrentOperationCount=1), 모든 작업이 완료된 경우에만 확인해야 했습니다.
구글링 "시리얼 NSoperation Queue가 FIFO인가?"라는 질문에 대한 답변으로 Apple 개발자의 이 진술을 찾았습니다.
모든 작업의 우선 순위가 동일하고(작업을 큐에 추가한 후에도 변경되지 않음) 모든 작업이 항상 - isReady==예, 작업 대기열에 입력될 때까지 일련의 NSO 작업 대기열은 FIFO가 됩니다.
크리스 케인 코코아 프레임워크, 애플
저의 경우 마지막 작업이 언제 큐에 추가되었는지 알 수 있습니다.그래서 마지막 작업이 추가된 후에 우선 순위가 낮은 다른 작업을 큐에 추가합니다. 이 작업은 큐가 비었다는 알림을 보내는 것 외에는 아무것도 수행하지 않습니다.이는 애플의 입장을 고려할 때 모든 작업이 완료된 후에만 한 번의 통지만 전송되도록 보장합니다.
마지막 작업을 감지할 수 없는 방식으로 작업이 추가되는 경우(즉, 비결정적인 방식), 위에서 언급한 KVO 접근 방식과 함께 추가 작업이 추가될 수 있는지 감지하기 위해 추가적인 가드 로직이 추가되어야 한다고 생각합니다.
:)
마지막으로 실행되도록 다른 모든 작업에 종속되는 NSO 작업을 추가하는 것은 어떻습니까?
하나의 대안은 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
});
iOS 13.0부터는 operationCount 및 operation 속성이 더 이상 사용되지 않습니다.대기열에 있는 작업 수를 직접 추적하고 작업이 모두 완료되면 알림을 실행하는 것도 간단합니다.이 예제는 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
}
}
아래는 쉬운 비동기 연산을 위한 연산의 하위 클래스입니다.
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.")
}
}
저는 이렇게 합니다.
대기열을 설정하고 작업 속성의 변경 사항을 등록합니다.
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
무슨 일이 일어나고 있다는 걸 보여주는 거죠 수 ...분명히 당신은 옷을 갈아입을 수 있습니다.
저는 카테고리를 이용해서 이렇게 하고 있습니다.
NSOperationQueue+Complete.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+Complete.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
해 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];
}
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!");
}
}];
참고로, 이것은 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")
}
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
iOS 13부터 https://developer.apple.com/documentation/foundation/operationqueue/3172534-addbarrierblock 을 이용할 수 있습니다.
대기열에서 모든 대기열 작업이 완료되면 블록을 호출하고, 블록이 완료될 때까지 후속 작업이 시작되지 않도록 합니다.
queue.addBarrierBlock {
}
를 수 .NSThread
. 때.NSOperationQueue
은 당신 자신의 수 h,다.
저는 다음과 같은 것을 생각하고 있습니다.
- (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 {}
작업 대기열 블록:
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)
}
}
콤바인과 함께 해결책을 찾고 계신다면, 저는 제 자신의 상태 대상을 듣고 말았습니다.
@Published var state: OperationState = .ready
var sub: Any?
sub = self.$state.sink(receiveValue: { (state) in
print("state updated: \(state)")
})
언급URL : https://stackoverflow.com/questions/1049001/get-notification-when-nsoperationqueue-finishes-all-tasks
'source' 카테고리의 다른 글
두 번째 인수가 메인()인 char *argv[]와 char **argv의 차이 (0) | 2023.10.14 |
---|---|
gcc는 수학을 제대로 포함하지 않습니다.h (0) | 2023.10.14 |
NA만 포함된 열을 삭제하는 방법은 무엇입니까? (0) | 2023.10.14 |
Xcode 4.2 기본 설정인 "무선 연결 장치 지원"의 기능은 무엇입니까? (0) | 2023.10.14 |
런타임에 long_query_time 변수를 변경할 수 없는 이유 (0) | 2023.10.14 |