dispatch_async(dispatch_get_main_queue(),^ {…}); 等到做完了?
我在我的应用程序中有一个场景,我想在一个方法中做一些耗时的任务,包括一些数据处理以及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 }); }
这真的有用吗? 这真的是一个很好的做法吗? 达到这个目标的最好方法是什么?
PS这三个操作都是相互关联的。
编辑:对不起,伙计们。 我错过了上面的代码中的一行 。 我的实际代码是这样的。
- (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 }); }); }
再次,我真的很抱歉的混乱。
不,它不会等待,你在这样做的方式是不好的做法。
dispatch_async
始终是asynchronous的 。 只是将所有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更新应该很快。
您必须将主队列分派到运行计算的块中。 例如(在这里我创build一个调度队列,不要使用全局):
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. }); });
当然,如果你创build了一个队列,如果你的目标是6.0之前的iOS版本,那么不要忘记dispatch_release
。
您提出的doCalculationsAndUpdateUIs
执行数据处理并将UI更新分派到主队列。 我们假设你在第一次调用doCalculationsAndUpdateUIs
发送了一个后台队列。
虽然在技术上很好,但是有点脆弱,这取决于你每次打电话时都记得把它发送到后台:相反,我会build议你把你的调度发送到后台,并从相同的内部发送回主队列方法,因为它使逻辑明确和更强大,等等。
因此,它可能看起来像:
- (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更新)asynchronous调度你的UI更新,或者与dispatch_sync
同步(在哪里等待UI更新)来说,问题是你为什么要这样做同步:你真的想放慢后台进程,因为它等待UI更新,或者你想要后台进程继续进行UI更新发生。
一般情况下,您会像在原始问题中一样使用dispatch_async
asynchronous调度UI更新。 是的,当然有特殊情况需要同步分派代码(例如,通过在主队列上执行所有更新来将更新同步到某个类属性),但通常情况下,只需调度UI更新asynchronous地进行。 如果同步调度代码可能会导致问题(如死锁),所以我的一般build议是,如果有一些迫切的需要,你可能应该只同步调度UI更新,否则你应该devise你的解决scheme,以便你可以asynchronous调度它们。
在回答你的问题时,这是否是“达到这个目标的最好方法”,我们很难说不清楚解决的业务问题。 例如,如果你可能多次调用这个doCalculationsAndUpdateUIs
,我可能会倾向于使用我自己的串行队列而不是并发的全局队列,以确保它们不会互相超越。 或者,如果您可能需要在用户closures场景或再次调用方法时取消此doCalculationsAndUpdateUIs
的function,则可能倾向于使用提供取消function的操作队列。 这完全取决于你想要达到的目标。
但是,一般来说,asynchronous调度复杂任务到后台队列,然后asynchronous调度UI更新回主队列的模式是非常普遍的。
如果你想运行一个独立的排队操作,而不关心其他并发操作,你可以使用全局并发队列:
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受到限制,线程的调度优先级设置为最低值)。
不,它不会等。
你可以使用performSelectorOnMainThread:withObject:waitUntilDone:
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. }); });
好的,有两种方法可以做到这一点:
// 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"); }); }); }