我的应用程序中有一个场景,我想在一个方法中做一些耗费时间的任务,其中包括一些数据处理和UI更新。我的方法看起来像这样,
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
// UI UPDATE 1
// DATA PROCESSING 2
// UI UPDATE 2
// DATA PROCESSING 3
// UI UPDATE 3
}
由于耗时我想在后台线程上进行数据处理,使用
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
但是由于数据处理和UI更新都采用相同的方法,我只想在主线程中使用,
移动UI更新dispatch_async(dispatch_get_main_queue(), ^{
最后我的方法看起来像这样,
- (void)doCalculationsAndUpdateUIs {
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 1
});
/* I expect the control to come here after UI UPDATE 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 2
});
/* I expect the control to come here after UI UPDATE 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 3
});
}
这真的有用吗?这真的是一个好习惯吗?实现这一目标的最佳方法是什么?
P.S。所有这三个操作都是相互关联的。
编辑:抱歉,伙计们。我错过了上述代码中的一行。我的实际代码看起来像这样。
- (void)doCalculationsAndUpdateUIs {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 1
});
/* I expect the control to come here after UI UPDATE 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 2
});
/* I expect the control to come here after UI UPDATE 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATE 3
});
});
}
再次,我真的为这种混乱道歉。
答案 0 :(得分:103)
不,它不等待,你在这个样本中做的方式不是好习惯。
dispatch_async
始终是异步。只是您将所有UI块排入同一队列,这样不同的块将按顺序运行,但与您的数据处理代码并行。
如果您希望更新等待,则可以改为使用dispatch_sync
。
// This will wait to finish
dispatch_sync(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
另一种方法是嵌套排队。我不会推荐它用于多个级别。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Background work
dispatch_async(dispatch_get_main_queue(), ^{
// Update UI
});
});
});
});
如果您需要更新UI以等待,那么您应该使用同步版本。有一个后台线程等待主线程是完全可以的。 UI更新应该非常快。
答案 1 :(得分:11)
您必须将主队列调度放在运行计算的块中。例如(这里我创建一个调度队列而不使用全局队列):
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
// Do some computation here.
// Update UI after computation.
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
});
当然,如果你创建了一个队列,如果你的目标是6.0之前的iOS版本,请不要忘记dispatch_release
。
答案 2 :(得分:8)
您建议的doCalculationsAndUpdateUIs
执行数据处理并将UI更新分派给主队列。我假设您在第一次调用它时已将doCalculationsAndUpdateUIs
调度到后台队列。
虽然技术上很好,但这有点脆弱,取决于你每次打电话时都记得要把它发送到后台:相反,我会建议你把你的调度发送到后台并从那里调回主队列。在同一方法中,因为它使逻辑明确且更健壮,等等。
因此它可能看起来像:
- (void)doCalculationsAndUpdateUIs {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
// DATA PROCESSING 1
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 1
});
/* I expect the control to come here after UI UPDATION 1 */
// DATA PROCESSING 2
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 2
});
/* I expect the control to come here after UI UPDATION 2 */
// DATA PROCESSING 3
dispatch_async(dispatch_get_main_queue(), ^{
// UI UPDATION 3
});
});
}
是否与dispatch_async
异步调度UI更新(后台进程不等待UI更新)或与dispatch_sync
同步(其中) 将等待UI更新),问题是为什么你想要同步这样做:你是否真的想在等待UI的同时放慢后台进程的速度更新,或者您希望在UI更新发生时继续进行后台处理。
通常,您会像在原始问题中使用的dispatch_async
异步调度UI更新。是的,确实存在需要同步调度代码的特殊情况(例如,您通过在主队列上对其执行所有更新来将更新同步到某个类属性),但通常情况下,您只需调度UI更新异步并继续。同步调度代码可能会导致问题(例如死锁),所以我的总法律顾问是,如果有一些迫切的需要,你应该只能同步调度UI更新,否则你应该设计你的解决方案,这样你就可以异步调度它们
在回答您关于这是否是“实现此目标的最佳方式”的问题时,我们很难在不了解更多有关正在解决的业务问题的情况下说出来。例如,如果您可能多次调用此doCalculationsAndUpdateUIs
,我可能倾向于使用自己的串行队列而不是并发全局队列,以确保它们不会相互跳过。或者,如果您可能需要能够在用户解除场景或再次调用该方法时取消此doCalculationsAndUpdateUIs
,那么我可能倾向于使用提供取消功能的操作队列。这完全取决于你想要达到的目标。
但是,通常,将复杂任务异步调度到后台队列然后异步调度UI更新回主队列的模式非常常见。
答案 3 :(得分:2)
如果要运行单个独立的排队操作而不关心其他并发操作,可以使用全局并发队列:
dispatch_queue_t globalConcurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
这将返回具有给定优先级的并发队列,如文档中所述:
DISPATCH_QUEUE_PRIORITY_HIGH分派给队列的项目将以高优先级运行,即队列将被安排在任何默认优先级或低优先级队列之前执行。
DISPATCH_QUEUE_PRIORITY_DEFAULT调度到队列的项目将以默认优先级运行,即在调度所有高优先级队列之后,但在安排任何低优先级队列之前,将调度队列执行。
DISPATCH_QUEUE_PRIORITY_LOW调度到队列的项目将以低优先级运行,即在调度所有默认优先级和高优先级队列后,将调度队列执行。
DISPATCH_QUEUE_PRIORITY_BACKGROUND调度到队列的项目将以后台优先级运行,即在调度所有优先级较高的队列后,队列将被调度执行,系统将根据setpriority在具有后台状态的线程上运行此队列上的项目( 2)(即磁盘I / O被限制,线程的调度优先级设置为最低值)。
答案 4 :(得分:1)
不,它不会等待。
您可以使用performSelectorOnMainThread:withObject:waitUntilDone:
。
答案 5 :(得分:1)
dispatch_queue_t queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(queue, ^{
// Do some computation here.
// Update UI after computation.
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI on the main thread.
});
});
答案 6 :(得分:0)
好的做法是:Dispatch Groups
dispatch_group_t imageGroup = dispatch_group_create();
dispatch_group_enter(imageGroup);
[uploadImage executeWithCompletion:^(NSURL *result, NSError* error){
// Image successfully uploaded to S3
dispatch_group_leave(imageGroup);
}];
dispatch_group_enter(imageGroup);
[setImage executeWithCompletion:^(NSURL *result, NSError* error){
// Image url updated
dispatch_group_leave(imageGroup);
}];
dispatch_group_notify(imageGroup,dispatch_get_main_queue(),^{
// We get here when both tasks are completed
});
答案 7 :(得分:-1)
好的,有两种方法可以做到这一点:
// GLOBAL_CONCURRENT_QUEUE
- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE
{
dispatch_queue_t globalConcurrentQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalConcurrentQ, ^{
// DATA PROCESSING 1
sleep(1);
NSLog(@"Hello world chekpoint 1");
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 1
sleep(1);
NSLog(@"Hello world chekpoint 2");
});
/* the control to come here after UI UPDATION 1 */
sleep(1);
NSLog(@"Hello world chekpoint 3");
// DATA PROCESSING 2
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 2
sleep(1);
NSLog(@"Hello world chekpoint 4");
});
/* the control to come here after UI UPDATION 2 */
sleep(1);
NSLog(@"Hello world chekpoint 5");
// DATA PROCESSING 3
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 3
sleep(1);
NSLog(@"Hello world chekpoint 6");
});
});
}
// SERIAL QUEUE
- (void)doCalculationsAndUpdateUIsWith_GlobalQUEUE
{
dispatch_queue_t serialQ = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_async(serialQ, ^{
// DATA PROCESSING 1
sleep(1);
NSLog(@"Hello world chekpoint 1");
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 1
sleep(1);
NSLog(@"Hello world chekpoint 2");
});
sleep(1);
NSLog(@"Hello world chekpoint 3");
// DATA PROCESSING 2
dispatch_sync(dispatch_get_main_queue(), ^{
// UI UPDATION 2
sleep(1);
NSLog(@"Hello world chekpoint 4");
});
});
}