当两个单独的NSFetchRequests都完成时采取行动 [英] Take action when two separate NSFetchRequests have both completed
问题描述
我使用Core Data的远程数据库,当我执行以下获取请求时,根据互联网连接,它可能需要一些时间。我想监控这两个请求,当他们完成后,无论成功还是失败 - 我想触发另一种方法。
FetchRequest 1:
self.managedObjectContext executeFetchRequest:fetchRequest1 onSuccess:^(NSArray * results){
// Succcess
[self.refreshControl endRefreshing];
} onFailure:^(NSError * error){
[self.refreshControl endRefreshing];
}];
FetchRequest 2:
[self.managedObjectContext executeFetchRequest:fetchRequest2 onSuccess:^(NSArray * results){
// Succcess
[self.refreshControl endRefreshing];
} onFailure:^(NSError * error){
[self.refreshControl endRefreshing];
}];
我想等待fetch请求 1 和 2
我可以使用 NSOperationQueue
来监控这两个块吗?如果没有,什么是最好的方式知道两个块都完成了?
当您使用依赖项执行异步任务时,您有以下几个选项:
-
最少的代码更改的最简单的解决方案是使用信号量:
//创建一个信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
//发起两个请求(完成后发送信号)
[self.managedObjectContext executeFetchRequest:fetchRequest1 onSuccess:^(NSArray * results){
[self.refreshControl endRefreshing];
dispatch_semaphore_signal(semaphore)
} onFailure:^(NSError * error){
[self.refreshControl endRefreshing];
dispatch_semaphore_signal(semaphore);
}];
[self.managedObjectContext executeFetchRequest:fetchRequest2 onSuccess:^(NSArray * results){
[self.refreshControl endRefreshing];
dispatch_semaphore_signal(semaphore);
} onFailure:^(NSError * error){
[self.refreshControl endRefreshing];
dispatch_semaphore_signal(semaphore);
}];
//现在创建任务以等待这两个完成信号量信号
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^ {
//等待来自两个提取的两个信号被发送
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
/ /现在做任何你想要的,当这两个请求完成
//如果你需要做任何UI更新或做任何与主队列同步,只是分派到主队列
dispatch_async(dispatch_get_main_queue(),^ {
NSLog(@all done);
});
});
这种方法可能最简单,但会带来各种限制。例如,这将绑定一个工作线程等待另外两个发送信号,所以你必须确信你没有太多的请求并发同时收集。您还必须确信这些请求将调用
onSuccess
或onFailure
,但永远不会调用。这也并不真正提供取消机会或自己限制并发程度的能力。 -
第二种方法是将异步请求替换为同步请求,然后可以使用
NSOperation
standardaddDependency
逻辑:NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSOperation * completionOperation = [NSBlockOperation blockOperationWithBlock:^ {
//完成操作
}];
NSOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^ {
// do fetch1 _synchronously_
}];
[queue addOperation:operation1];
NSOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^ {
// do fetch2 _synchronously_
}];
[queue addOperation:operation2];
[completionOperation addDependencies:@ [operation1,operation2]];
[queue addOperation:completionOperation];
此方法要求您的同步提取是线程安全的。
-
如果您没有使用同步再现,您可以使用此API。您可以将其添加到队列的第三种方法是使用您自己的并发
NSOperation
子类封装您的异步获取请求,而不会发出isfinished
,直到异步操作完成(也可以调用自己的onSuccess
和onFailure
块)。一旦你这样做,你可以使用setDependency
功能(如上一点所示)使你的第三个操作取决于另外两个完成。有关详细信息,请参阅
$ b $
b我希望我能提供一个更明确的答案,但我不熟悉你的并发管理上下文库所需的选项/约束。
I'm using a remote database with Core Data and when I execute the following fetch requests, depending on the internet connection, it can take some time. I'd like to monitor these two requests and, when they are complete -- whether successful or failed -- I'd like to trigger another method.
FetchRequest 1:
[self.managedObjectContext executeFetchRequest:fetchRequest1 onSuccess:^(NSArray *results) {
//Succcess
[self.refreshControl endRefreshing];
} onFailure:^(NSError *error) {
[self.refreshControl endRefreshing];
}];
FetchRequest 2:
[self.managedObjectContext executeFetchRequest:fetchRequest2 onSuccess:^(NSArray *results) {
//Succcess
[self.refreshControl endRefreshing];
} onFailure:^(NSError *error) {
[self.refreshControl endRefreshing];
}];
I would like to wait until fetch requests 1 and 2 are both complete before calling another method.
Can I use NSOperationQueue
to monitor both blocks? If not, what's the best way to know when both blocks have completed?
When you have asynchronous tasks with dependencies, you have a couple of options:
The simplest solution with the least code changes is to use a semaphore:
// create a semaphore dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); // initiate two requests (signaling when done) [self.managedObjectContext executeFetchRequest:fetchRequest1 onSuccess:^(NSArray *results) { [self.refreshControl endRefreshing]; dispatch_semaphore_signal(semaphore); } onFailure:^(NSError *error) { [self.refreshControl endRefreshing]; dispatch_semaphore_signal(semaphore); }]; [self.managedObjectContext executeFetchRequest:fetchRequest2 onSuccess:^(NSArray *results) { [self.refreshControl endRefreshing]; dispatch_semaphore_signal(semaphore); } onFailure:^(NSError *error) { [self.refreshControl endRefreshing]; dispatch_semaphore_signal(semaphore); }]; // now create task to to wait for these two to finish signal the semaphore dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // wait for the two signals from the two fetches to be sent dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // now do whatever you want when those two requests finish // if you need to do any UI update or do any synchronizing with the main queue, just dispatch this to the main queue dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"all done"); }); });
This approach might be easiest, but entails all sorts of limitations. For example, this will tie up a worker thread waiting for the other two to send the signal, so you'd have to be confident that you don't have too many of these collections of requests going concurrently. You also have to be confident that these requests will call either
onSuccess
oronFailure
, but never both and always one. This also doesn't really offer an cancellation opportunities or the ability to constrain the degree of concurrency yourself. But you can do the above with minimal code changes.The second approach would be to replace your asynchronous requests with synchronous ones, that you can then use the
NSOperation
standardaddDependency
logic:NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{ // completion operation }]; NSOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ // do fetch1 _synchronously_ }]; [queue addOperation:operation1]; NSOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ // do fetch2 _synchronously_ }]; [queue addOperation:operation2]; [completionOperation addDependencies:@[operation1, operation2]]; [queue addOperation:completionOperation];
This approach requires that your synchronous fetches are thread-safe. I'm not familiar with this API you're using, so I can't speak to that.
If you don't have synchronous renditions of your fetch requests that you could add to the queue, the third approach would be to wrap your asynchronous fetch requests with your own concurrent
NSOperation
subclass that won't signalisFinished
until the asynchronous operation is done (and also presumably call your ownonSuccess
andonFailure
blocks). Once you do that, you can then use thesetDependency
functionality (as illustrated in the prior point) to make your third operation dependent upon the other two finishing. For more information, see Configuring Operations for Concurrent Execution section of the Concurrency Programming Guide.
I wish I could provide a more definitive answer, but I'm not familiar enough with the options/constraints entailed by your concurrent managed context library.
这篇关于当两个单独的NSFetchRequests都完成时采取行动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!