NSOperationQueue 在所有操作完成之前完成

NSOperationQueue Is Finished Before All Operations Are Completed

我有 x 号不同的任务需要 运行。每个任务负责连接到互联网、下载一些数据、处理该数据,然后保存该数据。

它们都在后台线程上同时 运行。虽然我对他们很好运行一个接一个。

当所有 4 个任务都完成后,我希望收到通知,它们已以某种方式完成。

但是,NSOperationQueue 在任何其他实际操作开始之前就关闭了已完成的队列。

我的当前设置日志:

fetchMyData: completionOperation YES

FILE: MySet1 CREATED

FILE: MySet2 CREATED

FILE: MySet3 CREATED

FILE: MySet4 CREATED

是否有更好的方法将操作链接在一起并在触发最终操作之前完全等待所有操作完成?

这是我正在做的事情:

- (void) timeToUpdate {
    [self fetchMyData:^(BOOL done) {
        NSLog(@"fetchMyData: completionOperation : %@", done ? @"YES":@"NO");
    }];


- (void) fetchMyData: (void(^)(BOOL done)) completion {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"fetchMyData: completionOperation");
        completion(true);
    }];
    
    NSOperation *operation;
    

    operation = [NSBlockOperation blockOperationWithBlock:^{
        [self downloadDataSet:@"MySet1"];
    }];
    
    [completionOperation addDependency:operation];
    [queue addOperation:operation];
    

    operation = [NSBlockOperation blockOperationWithBlock:^{
        [self downloadDataSet:@"MySet2"];
    }];
    
    [completionOperation addDependency:operation];
    [queue addOperation:operation];
    

    operation = [NSBlockOperation blockOperationWithBlock:^{
        [self downloadDataSet:@"MySet3"];
    }];
    
    [completionOperation addDependency:operation];
    [queue addOperation:operation];
    

    operation = [NSBlockOperation blockOperationWithBlock:^{
        [self downloadDataSet:@"MySet4"];
    }];
    
    [completionOperation addDependency:operation];
    [queue addOperation:operation];
    
    [queue addOperation:completionOperation];
}

//The below is just abstract sudo code that works and does its job of downloading, processing and saving the data.

- (void) downloadDataSet:(SomeSet) {
    
    [NSURLSessionUploadTask  uploadTaskWithRequest:task 
                                    fromData: Someset 
                                   completionHandler: {
                    process(set);
    }]
}

- process:(data) {
    doSomethignWithData(data)
//then
    write(data)

}
- write(data) {
    //save to file system, sql, coredata whatever
    
    if(success){
        NSLog(@"FILE: data CREATED");
    }
}

问题是您的操作正在调用 uploadTaskWithRequest:fromData: completionHandler:,这是一个在其作业完成前 return 秒的异步操作。你应该做的是研究使用 asynchronous NSOperation;基本上,您将 isAsynchronous 方法覆盖为 return YES,然后在调用 uploadTaskWithRequest:fromData: completionHandler: 的完成处理程序时设置操作的 finished 属性 .这样,任何依赖于您的下载操作的操作都会等到它们实际完成后再触发。

这是我使用的一组方便的异步 NSOperation 子类;它是 Swift,而不是 Objective-C,但它说明了这个概念,并且应该为您提供一个不错的伪代码来构建您自己的实现。

open class AsyncOperation: Operation {
    override open var isAsynchronous: Bool { return true }
    override open var isExecuting: Bool { return self.state == .started }
    override open var isFinished: Bool { return self.state == .done }

    private enum State {
        case initial
        case started
        case done
    }

    private var state: State = .initial {
        willSet {
            // due to a legacy issue, these have to be strings. Don't make them key paths.
            self.willChangeValue(forKey: "isExecuting")
            self.willChangeValue(forKey: "isFinished")
        }
        didSet {
            self.didChangeValue(forKey: "isFinished")
            self.didChangeValue(forKey: "isExecuting")
        }
    }

    public init(name: String? = nil) {
        super.init()

        if #available(macOS 10.10, *) {
            self.name = name
        }
    }

    final override public func start() {
        self.state = .started

        self.main {
            if case .done = self.state {
                fatalError("AsyncOperation completion block called twice")
            }

            self.state = .done
        }
    }

    final override public func main() {}

    open func main(completionHandler: @escaping () -> ()) {
        fatalError("Subclass must override main(completionHandler:)")
    }
}

open class AsyncBlockOperation: AsyncOperation {
    private let closure: (_ completionHandler: @escaping () -> ()) -> ()

    public init(name: String? = nil, closure: @escaping (_ completionHandler: @escaping () -> ()) -> ()) {
        self.closure = closure
        super.init(name: name)
    }

    override open func main(completionHandler: @escaping () -> ()) {
        self.closure(completionHandler)
    }
}

或者,您可以避免使用 NSOperation 并使用 dispatch_group_t 来获得类似的行为;最初调用 dispatch_group_enter(),在下载任务的完成处理程序中调用 dispatch_group_leave(),并使用 dispatch_notify() 到 运行 你的完成块。