【问题标题】:NSOperationQueue Is Finished Before All Operations Are CompletedNSOperationQueue 在所有操作完成之前完成
【发布时间】:2018-03-09 17:42:56
【问题描述】:

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

它们都在后台线程上同时运行。虽然我可以让他们一个接一个地跑。

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

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

当前设置的我的日志:

fetchMyData:completionOperation YES

文件:MySet1 已创建

文件:MySet2 已创建

文件:MySet3 已创建

文件:MySet4 已创建

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

这是我正在做的事情:

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

【问题讨论】:

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


    【解决方案1】:

    问题在于您的操作正在调用uploadTaskWithRequest:fromData: completionHandler:,这是一个异步操作,它在其作业完成之前返回。你应该做的是研究使用 asynchronous NSOperation;基本上你重写isAsynchronous 方法以返回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() 运行您的完成块。

    【讨论】:

    • 这是在 Swift 中的。你有 Obj-c 的例子吗?
    • @user-44651 不,但正如我所说,您可以将其用作体面的伪代码来构建您自己的实现。
    • 请问这个怎么用? @CharlesSrstka
    • 好东西!一些伪示例代码会很有用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 2014-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多