รับการแจ้งเตือนเมื่อ NSOperationQueue ทำงานทั้งหมดเสร็จสิ้น


93

NSOperationQueueมีwaitUntilAllOperationsAreFinishedแต่ฉันไม่ต้องการรอพร้อมกัน ฉันแค่ต้องการซ่อนตัวบ่งชี้ความคืบหน้าใน UI เมื่อคิวเสร็จสิ้น

วิธีที่ดีที่สุดในการทำสิ่งนี้คืออะไร?

ฉันไม่สามารถส่งการแจ้งเตือนจากNSOperations ของฉันได้เพราะฉันไม่รู้ว่าการแจ้งเตือนใดจะเป็นครั้งสุดท้ายและ[queue operations]อาจยังไม่ว่างเปล่า (หรือแย่กว่านั้นคือการเติมใหม่) เมื่อได้รับการแจ้งเตือน


ตรวจสอบว่าคุณกำลังใช้ GCD ใน swift 3 หรือไม่stackoverflow.com/a/44562935/1522584
Abhijith

คำตอบ:


167

ใช้ 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 ดังนั้นจึงยังมีประโยชน์สำหรับความเข้ากันได้แบบย้อนหลัง


26
ฉันขอโต้แย้งว่าคุณควรใช้ตัวเข้าถึงคุณสมบัติเนื่องจากมีการห่อหุ้มที่พิสูจน์แล้วในอนาคต (หากคุณตัดสินใจเช่นที่จะเริ่มต้นคิวอย่างเกียจคร้าน) การเข้าถึงคุณสมบัติโดยตรงโดย ivar ถือได้ว่าเป็นการเพิ่มประสิทธิภาพก่อนกำหนด แต่จริงๆแล้วขึ้นอยู่กับบริบทที่แน่นอน เวลาที่ประหยัดได้จากการเข้าถึงอสังหาริมทรัพย์โดยตรงผ่านไอวาร์มักจะน้อยมากเว้นแต่คุณจะอ้างถึงคุณสมบัตินั้นมากกว่า 100-1000 ครั้งต่อวินาที (เป็นการคาดเดาที่หยาบคายอย่างไม่น่าเชื่อ)
Nick Forge

2
ถูกล่อลวงให้ลดคะแนนเนื่องจากการใช้ KVO ที่ไม่ดี อธิบายการใช้งานที่เหมาะสม: dribin.org/dave/blog/archives/2008/09/24/proper_kvo_usage
Nikolai Ruhe

19
@NikolaiRuhe คุณถูกต้อง - การใช้รหัสนี้เมื่อคลาสย่อยคลาสที่ตัวเองใช้ KVO เพื่อสังเกตoperationCountบนNSOperationQueueวัตถุเดียวกันอาจนำไปสู่จุดบกพร่องซึ่งในกรณีนี้คุณจะต้องใช้อาร์กิวเมนต์บริบทอย่างถูกต้อง มันไม่น่าเกิดขึ้น แต่เป็นไปได้แน่นอน (การสะกดปัญหาที่แท้จริงมีประโยชน์มากกว่าการเพิ่ม snark + ลิงค์)
Nick Forge

6
พบว่ามีความคิดที่น่าสนใจที่นี่ ฉันใช้สิ่งนั้นเพื่อคลาสย่อย NSOperationQueue เพิ่มคุณสมบัติ NSOperation 'finalOpearation' ที่ตั้งค่าตามการดำเนินการแต่ละอย่างที่เพิ่มลงในคิว เห็นได้ชัดว่าต้องแทนที่ addOperation: เพื่อทำเช่นนั้น ยังเพิ่มโปรโตคอลที่ส่งข้อความไปยังผู้รับมอบสิทธิ์เมื่อ finalOperation เสร็จสิ้น ได้รับการทำงานจนถึงปัจจุบัน
pnizzle

1
ดีกว่าเยอะ! ฉันจะมีความสุขมากที่สุดเมื่อมีการระบุตัวเลือกและ removeObserver: call ถูกห่อด้วย @ try / @ catch - มันไม่เหมาะ แต่เอกสารของ apple ระบุว่าไม่มีความปลอดภัยเมื่อเรียก removeObserver: ... if วัตถุไม่มีการลงทะเบียนผู้สังเกตการณ์แอปพลิเคชันจะหยุดทำงาน
Austin

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>

คุณควรทราบว่าหากมีการเพิ่มการดำเนินการ "สั้น" จำนวนหนึ่งลงในคิวคุณอาจเห็นลักษณะการทำงานนี้แทน (เนื่องจากการดำเนินการเริ่มต้นโดยเป็นส่วนหนึ่งของการเพิ่มลงในคิว):

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) และเมื่อดำเนินการเสร็จสิ้นทั้งหมดเท่านั้น

Googling ฉันพบคำสั่งนี้จากนักพัฒนา Apple เพื่อตอบคำถาม "is a serial NSoperationQueue FIFO?" -

หากการดำเนินการทั้งหมดมีลำดับความสำคัญเท่ากัน (ซึ่งไม่มีการเปลี่ยนแปลงหลังจากเพิ่มการดำเนินการลงในคิว) และการดำเนินการทั้งหมดจะเสมอ - isReady == ใช่เมื่อถึงเวลาที่พวกเขาใส่ในคิวการดำเนินการดังนั้น NSOperationQueue แบบอนุกรมคือ FIFO

กรอบ Chris Kane Cocoa, Apple

ในกรณีของฉันเป็นไปได้ที่จะทราบเมื่อมีการเพิ่มการดำเนินการล่าสุดลงในคิว ดังนั้นหลังจากเพิ่มการดำเนินการล่าสุดฉันจึงเพิ่มการดำเนินการอื่นในคิวที่มีลำดับความสำคัญต่ำกว่าซึ่งไม่ได้ทำอะไรเลยนอกจากส่งการแจ้งเตือนว่าคิวว่าง จากคำชี้แจงของ Apple สิ่งนี้ทำให้มั่นใจได้ว่าจะมีการส่งหนังสือแจ้งเพียงครั้งเดียวหลังจากการดำเนินการทั้งหมดเสร็จสิ้น

หากมีการเพิ่มการดำเนินการในลักษณะที่ไม่อนุญาตให้ตรวจจับการดำเนินการสุดท้าย (เช่นไม่กำหนดปัจจัย) ฉันคิดว่าคุณต้องใช้แนวทาง KVO ที่กล่าวถึงข้างต้นโดยเพิ่มตรรกะการป้องกันเพิ่มเติมเพื่อพยายามตรวจจับว่าเพิ่มเติม อาจมีการเพิ่มการดำเนินการ

:)


สวัสดีคุณรู้หรือไม่ว่าจะเป็นไปได้อย่างไรและจะได้รับการแจ้งเตือนเมื่อแต่ละการดำเนินการในคิวสิ้นสุดลงโดยใช้ NSOperationQueue กับ maxConcurrentOperationCount = 1
Sefran2

@fran: ฉันจะให้การดำเนินการโพสต์การแจ้งเตือนเมื่อเสร็จสิ้น ด้วยวิธีนี้โมดูลอื่น ๆ สามารถลงทะเบียนเป็นผู้สังเกตการณ์และตอบสนองเมื่อแต่ละส่วนเสร็จสมบูรณ์ หาก @selector ของคุณใช้วัตถุการแจ้งเตือนคุณสามารถเรียกคืนวัตถุที่โพสต์การแจ้งเตือนได้อย่างง่ายดายในกรณีที่คุณต้องการรายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่ op เพิ่งทำเสร็จ
ซอฟต์แวร์พัฒนา

17

วิธีการเพิ่ม NSOperation ที่ขึ้นอยู่กับคนอื่น ๆ ทั้งหมดเพื่อให้ทำงานได้นาน?


1
อาจได้ผล แต่เป็นวิธีแก้ปัญหาที่มีน้ำหนักมากและจะต้องจัดการกับความเจ็บปวดหากคุณต้องการเพิ่มงานใหม่ลงในคิว
Kornel

นี่เป็นสิ่งที่สง่างามมากและเป็นสิ่งที่ฉันชอบที่สุด! คุณโหวตของฉัน
Yariv Nissim

1
โดยส่วนตัวนี่เป็นทางออกที่ฉันชอบ คุณสามารถสร้าง NSBlockOperation แบบง่ายๆสำหรับบล็อกการทำให้สมบูรณ์ซึ่งขึ้นอยู่กับการดำเนินการอื่น
ปูนเศรษฐี

คุณอาจพบปัญหาที่ NSBlockOperation ไม่ถูกเรียกเมื่อคิวถูกยกเลิก ดังนั้นคุณต้องดำเนินการของคุณเองซึ่งจะสร้างข้อผิดพลาดเมื่อถูกยกเลิกและเรียกบล็อกด้วยพารามิเตอร์ข้อผิดพลาด
malhal

นี่คือคำตอบที่ดีที่สุด!
ดักสัตว์

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

5

นี่คือวิธีที่ฉันทำ

ตั้งค่าคิวและลงทะเบียนสำหรับการเปลี่ยนแปลงในคุณสมบัติการดำเนินงาน:

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แสดงให้เห็นว่ามีบางสิ่งเกิดขึ้น เห็นได้ชัดว่าคุณสามารถเปลี่ยนให้เหมาะกับ ...


2
การforวนซ้ำนั้นอาจมีราคาแพง (ถ้าคุณยกเลิกการดำเนินการทั้งหมดในครั้งเดียวจะไม่ได้รับประสิทธิภาพกำลังสองเมื่อกำลังล้างคิว)
Kornel

ดี แต่โปรดระวังเธรดเนื่องจากตามเอกสาร: "... การแจ้งเตือน KVO ที่เกี่ยวข้องกับคิวการดำเนินการอาจเกิดขึ้นในเธรดใด ๆ " อาจเป็นไปได้ว่าคุณต้องย้ายขั้นตอนการดำเนินการไปยังคิวการดำเนินการหลักก่อนอัปเดตสปินเนอร์
Igor Vasilev

4

ในฐานะของiOS 13.0ที่operationCountและการดำเนินงานคุณสมบัติจะเลิก เพียงแค่ติดตามจำนวนการดำเนินการในคิวของคุณด้วยตัวคุณเองและปิดการแจ้งเตือนเมื่อดำเนินการเสร็จสิ้น ตัวอย่างนี้ใช้ได้กับคลาสย่อยของการทำงานแบบอะซิงโครนัสด้วย

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

}


ซึ่งเป็นdecrementOperationCount()วิธีการเรียก?
iksnae

@iksnae - ฉันได้ปรับปรุงคำตอบของฉันกับ sublcass ของการดำเนินงาน ฉันใช้degmentOperationCount ()ภายในdidSetของตัวแปรสถานะของฉัน หวังว่านี่จะช่วยได้!
Caleb Lindsey

3

ฉันใช้หมวดหมู่เพื่อทำสิ่งนี้

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 + เสร็จม

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


ทำไมนี้เป็นเสร็จ ? NSOperationQueue ไม่เสร็จสมบูรณ์ - เพียงว่างเปล่า สามารถป้อนสถานะว่างได้หลายครั้งในช่วงอายุของ NSOperationQueue
CouchDeveloper

สิ่งนี้จะไม่ได้ผลถ้า op1 และ op2 เสร็จสิ้นก่อนที่จะเรียกใช้ setCompletion
malhal

คำตอบที่ยอดเยี่ยมมีข้อแม้เพียง 1 ข้อที่การบล็อกเสร็จสิ้นจะถูกเรียกเมื่อคิวเสร็จสิ้นพร้อมกับเริ่มการดำเนินการทั้งหมด กำลังเริ่มดำเนินการ! = การดำเนินการเสร็จสมบูรณ์
Saqib Saud

อืมคำตอบเก่า แต่ฉันพนันwaitUntilFinishedได้เลยว่าควรจะเป็นYES
brandonscript

2

สิ่งที่เกี่ยวกับการใช้ KVO เพื่อสังเกตoperationCountคุณสมบัติของคิว? จากนั้นคุณจะได้ยินเกี่ยวกับเรื่องนี้เมื่อคิวว่างเปล่าและเมื่อคิวว่างเปล่า การจัดการกับตัวบ่งชี้ความคืบหน้าอาจทำได้ง่ายเพียงแค่ทำสิ่งต่อไปนี้

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

สิ่งนี้ได้ผลสำหรับคุณหรือไม่? ในการประยุกต์ใช้ของฉันNSOperationQueueจาก 3.1 บ่นว่ามันไม่ได้เป็นไปตาม KVO operationCountสำหรับคีย์
zoul

ฉันไม่ได้ลองวิธีแก้ปัญหานี้ในแอปจริงๆไม่ใช่ ไม่สามารถพูดได้ว่า OP ทำได้หรือไม่ แต่เอกสารระบุชัดเจนว่าควรใช้งานได้ ฉันจะส่งรายงานข้อบกพร่อง developer.apple.com/iphone/library/documentation/Cocoa/…
Sixten Otto

ไม่มีคุณสมบัติ operationCount บน NSOperationQueue ใน iPhone SDK (อย่างน้อยไม่เท่ากับ 3.1.3) คุณต้องดูที่หน้าเอกสาร Max OS X ( developer.apple.com/Mac/library/documentation/Cocoa/Reference/… )
Nick Forge

1
เวลาเยียวยาบาดแผลทั้งหมด ... และบางครั้งก็ตอบผิด สำหรับ iOS 4 operationCountคุณสมบัตินี้มีอยู่
Sixten Otto

2

เพิ่มการดำเนินการสุดท้ายเช่น:

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

}

3
เมื่อมีการดำเนินการพร้อมกันแล้วมันเป็นแนวทางที่ไม่ถูกต้อง
Marcin

2
และเมื่อคิวถูกยกเลิกการดำเนินการสุดท้ายนี้จะไม่เริ่มต้นด้วยซ้ำ
malhal

2

ด้วย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!");
    }
}];

1

FYI, คุณสามารถบรรลุนี้กับ 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")
}

iOS เวอร์ชันขั้นต่ำที่จะใช้นี้คืออะไร?
Nitesh Borad

สามารถใช้ได้ตั้งแต่ swift 3, iOS 8 หรือสูงกว่า
Abhijith

0

คุณสามารถสร้างใหม่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"];
}

ดูเหมือนโง่เล็กน้อยที่จะสร้างเธรดเพียงเพื่อให้เข้านอน
Kornel

ฉันเห็นด้วย. ถึงกระนั้นฉันก็หาวิธีอื่นไม่ได้
pgb

คุณจะมั่นใจได้อย่างไรว่ามีเพียงเธรดเดียวเท่านั้นที่รออยู่? ฉันคิดถึงเรื่องธง แต่ต้องได้รับการปกป้องจากสภาพการแข่งขันและฉันก็ใช้ NSLock มากเกินไปสำหรับรสนิยมของฉัน
Kornel

ฉันคิดว่าคุณสามารถรวม NSOperationQueue ไว้ในวัตถุอื่นได้ เมื่อใดก็ตามที่คุณจัดคิว NSOperation คุณจะต้องเพิ่มจำนวนและเปิดเธรด เมื่อใดก็ตามที่เธรดสิ้นสุดลงคุณจะลดจำนวนนั้นลงทีละตัว ฉันกำลังคิดถึงสถานการณ์ที่คุณสามารถจัดคิวทุกอย่างไว้ล่วงหน้าแล้วเริ่มคิวดังนั้นคุณจะต้องมีเธรดรอเพียงชุดเดียว
pgb

0

หากคุณใช้Operationนี้เป็นคลาสพื้นฐานของคุณคุณสามารถส่งต่อ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")
}

1
ค่าประเภท 'OperationQueue' ไม่มีสมาชิก 'whenEmpty'
Dale

@Dale ถ้าคุณคลิกที่ลิงค์มันจะนำคุณไปยังหน้า github ที่มีคำอธิบายทุกอย่าง ถ้าฉันจำได้ถูกต้องคำตอบถูกเขียนขึ้นเมื่อมูลนิธิ OperationQueue ยังคงเรียกว่า NSOperationQueue ดังนั้นจึงมีความคลุมเครือน้อยกว่า
user1244109

แย่จัง ... ฉันสรุปเท็จว่า "OperationQueue" ข้างบนคือ "OperationQueue" ของ Swift 4
Dale

0

ไม่มี 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)
    }
}

0

ถ้าคุณมาที่นี่กำลังมองหาวิธีแก้ปัญหาด้วยการรวม - ฉันก็แค่ฟังวัตถุสถานะของฉันเอง

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

sub = self.$state.sink(receiveValue: { (state) in
 print("state updated: \(state)")
})
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.