所有操作完成前完成NSOperationQueue

时间:2018-03-09 17:42:56

标签: ios objective-c objective-c-blocks nsoperationqueue nsoperation

我有 x 号码需要运行的不同任务。每项任务都负责上网,下载一些数据,处理数据,然后保存数据。

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

当所有4个任务完成后,我想通知他们已经以某种方式完成了。

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

使用当前设置记录我的日志:

  

fetchMyData:completionOperation YES

     

FILE:MySet1 CREATED

     

文件:MySet2已创建

     

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

1 个答案:

答案 0 :(得分:3)

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

这是我使用的一组方便的异步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()运行完成功能块。