سؤال

NSOperationQueue وقد waitUntilAllOperationsAreFinished, ولكن أنا لا أريد أن انتظر بشكل متزامن من أجل ذلك.أنا فقط أريد أن إخفاء مؤشر التقدم في واجهة المستخدم عند طابور التشطيبات.

ما هي أفضل طريقة لتحقيق هذا الهدف ؟

لا أستطيع إرسال إخطارات من NSOperations, لأنني لا أعرف أي واحد هو الذهاب الى تكون الأخيرة ، [queue operations] قد لا يكون فارغا حتى الآن (أو ما هو أسوأ - اسكانها) عند استلام إشعار.

هل كانت مفيدة؟

المحلول

استخدم المنظمة من لمراقبة الممتلكات operations من قائمة الانتظار الخاصة بك، ثم يمكنك معرفة ما إذا كان قد أكمل قائمة الانتظار الخاصة بك عن طريق التحقق من [queue.operations count] == 0.

وفي مكان ما في ملف كنت تريد ان تفعل المنظمة من في، إعلان سياق المنظمة من مثل هذا (<لأ href = "http://www.dribin.org/dave/blog/archives/2008/09/24/ proper_kvo_usage / "يختلط =" noreferrer "> مزيد من المعلومات ):

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

و

وإضافة: دائرة الرقابة الداخلية 4.0 لديها خاصية NSOperationQueue.operationCount، والتي وفقا للمستندات غير متوافقة المنظمة من. وهذا الجواب لا تزال تعمل في دائرة الرقابة الداخلية 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 == نعم في الوقت الذي تحصل على وضعها في قائمة الانتظار العملية، ثم المسلسل   NSOperationQueue هو FIFO.

     

وكريس كين   الكاكاو أطر، أبل

في حالتي كان من الممكن أن تعرف متى تم إضافة العملية الأخيرة إلى قائمة الانتظار. حتى بعد يتم إضافة العملية الأخيرة، أود أن أضيف عملية أخرى إلى قائمة الانتظار، الأولوية الدنيا، التي لا تفعل شيئا ولكن إرسال إعلام أن قائمة الانتظار قد أفرغ. نظرا بيان أبل، وهذا يضمن أن يتم إرسال سوى إشعار واحد فقط بعد أن تم الانتهاء من جميع العمليات.

إذا يتم إضافتها العمليات بطريقة لا تسمح الكشف عن آخر واحد، (أي غير القطعية) ثم أعتقد أن لديك للذهاب مع المنظمة من النهج المذكورة أعلاه، مع المنطق حارس إضافي أضاف في محاولة ل الكشف عن حالة يمكن إضافة المزيد من العمليات.

:)

وماذا عن إضافة NSOperation التي تعتمد على كل الآخرين لذلك سيتم تشغيل آخر؟

وهذا هو كيف أفعل ذلك.

وإعداد قائمة الانتظار، وتسجيل التغيرات في الملكية العمليات:

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+الانتهاء.ح

//
//  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+الانتهاء.م

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

ويمكنك إنشاء 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")
}

وبدون المنظمة من

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)
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top