我有 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");
}
}
答案 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()
运行完成功能块。