在主线程上执行耗时的操作如何影响UI刷新

时间:2019-05-21 15:38:09

标签: ios objective-c

主线程上的同步时间可能导致主线程阻塞。这是UI延迟的原因吗?如果我正在主线程上执行异步,耗时的操作,这是否会影响主线程上的UI流程?同样,必须将耗时的操作放在子线程异步操作中。

  1. 视图刷新代码是否正在等待耗时的操作完成才能执行,原因是UI不流畅?

    [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
    - (void)syncMain {
        dispatch_queue_t queue = dispatch_get_main_queue();
    
        dispatch_sync(queue, ^{
            // 追加任务1
            for (int i = 0; i < 2; ++i) {
                [NSThread sleepForTimeInterval:2];   // Simulated time-consuming operation
                NSLog(@"1---%@",[NSThread currentThread]);      // on the main thread
            }
        });
        self.testImage=[UIImage imageNamed:xxx];//Waiting for a time-consuming operation to complete, causing a UI refresh block
    }
    
  2. 在主线程上执行异步耗时的操作是否会影响UI的流动性?

    - (void)asyncMain {    
        dispatch_queue_t queue = dispatch_get_main_queue();
    
        dispatch_async(queue, ^{
            // 追加任务1
            for (int i = 0; i < 2; ++i) {
                [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation
                NSLog(@"1---%@",[NSThread currentThread]);      // on the main thread,too
            }
        });
    
        self.testImage=[UIImage imageNamed:xxx];//Execute code now
    }
    
  3. 是否只能在子线程上异步执行耗时的操作以保持UI完整?

    - (void)asyncChild {    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                       [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation
                    dispatch_async(dispatch_get_main_queue(), ^{
                    self.testImage=[UIImage imageNamed:xxx];
                        });
                });
    }
    

3 个答案:

答案 0 :(得分:0)

您能举例说明遇到问题的某些操作吗?

如果主线程忙于做其他事情而不是UI相关的事情,那么是的,您可能会遇到缓慢或无法使用的UI。

通常,应该在后台线程上执行耗时或同步的操作,以避免此问题。但是请注意,必须在主线程上执行与UI / UIKit相关的操作,否则您的应用程序可能会崩溃。

答案 1 :(得分:0)

如果您在主线程上并向异步线程分派一些东西,那么除非该线程消耗了设备上的所有资源,否则它不会对UI产生影响。

当异步线程完成后,您将使用异步操作的结果更新UI,这将使您满意。如果您忘记将UI更新移到主线程上,则UI可能会在某个随机时间间隔更新,或者根本不会更新。这就是大多数人进行异步操作时得到的。

答案 2 :(得分:0)

通常,异步分发一些东西到后台队列不会对UI产生实质性影响。这就是我们这样做的原因,以最大程度地减少对用户界面的影响。

如果您看到时滞,则可能发生在以下情况:

  • 后台线程意外执行了一些UI更新,忽略了将其分派回主线程;

  • 后台作业将一系列级联的任务调度到并发的全局队列中,耗尽了非常有限的可用工作线程;

  • 异步作业本身就是使用主线程进行任何耗时的操作,或者

  • 后台任务非常繁琐,以至于耗尽了所有系统资源(这是非常不寻常的)。


在编辑中,提供一些示例:

  
      
  1. 视图刷新代码是否正在等待耗时的操作完成才能执行,原因是UI不流畅?

    [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
    
    - (void)syncMain {
        dispatch_queue_t queue = dispatch_get_main_queue();
    
        dispatch_sync(queue, ^{
            // 追加任务1
            for (int i = 0; i < 2; ++i) {
                [NSThread sleepForTimeInterval:2];   // Simulated time-consuming operation
                NSLog(@"1---%@",[NSThread currentThread]);      // on the main thread
            }
        });
        self.testImage=[UIImage imageNamed:xxx];//Waiting for a time-consuming operation to complete, causing a UI refresh block
    }
    
  2.   

当您将块分配回主线程时,您不想包括这些耗时的任务,否则会对UX产生不利影响。在调度到主队列之前,请在后台线程上执行那些耗时的任务。

[NSThread detachNewThreadSelector:@selector(asyncMain) toTarget:self withObject:nil];

- (void)asyncMain {    
    dispatch_queue_t queue = dispatch_get_main_queue();

    for (int i = 0; i < 2; ++i) {
        [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation
        NSLog(@"1---%@",[NSThread currentThread]);      // still on the background thread
    }

    dispatch_async(queue, ^{            
        self.testImage=[UIImage imageNamed:xxx];//Execute code now
    });
}

并且没有理由将更新同步发送回主队列。异步分发这些更新,为什么后台线程要等待UI更新完成?

我个人建议不要使用detachNewThreadSelector,而要坚持使用GCD(就像您的第三个示例一样):

- (void)someTaskAndUpdateUI {    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation
            NSLog(@"1---%@",[NSThread currentThread]); // but on background queue
        }

        dispatch_async(dispatch_get_main_queue(), ^{            
            self.testImage=[UIImage imageNamed:xxx];//Execute code now
        });
    });
}
  
      
  1. 在主线程上执行异步耗时的操作是否会影响UI的流动性?

    - (void)asyncMain {    
        dispatch_queue_t queue = dispatch_get_main_queue();
    
        dispatch_async(queue, ^{
            // 追加任务1
            for (int i = 0; i < 2; ++i) {
                [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation
                NSLog(@"1---%@",[NSThread currentThread]);      // on the main thread,too
            }
        });
    
        self.testImage=[UIImage imageNamed:xxx];//Execute code now
    }
    
  2.   

您异步地将其分派到主线程而不是同步地分派的事实是无关紧要的。问题不在于您如何分配到主队列,而在于您分配了什么。您分派给主线程的代码执行的任务很慢,这意味着在执行慢任务时主线程无法执行其他任何操作。那就是对用户界面造成不利影响的原因。

因此解决方案与上面的解决方案一样,您要在将这些耗时的任务分派到主队列之前执行这些任务。尽管从主线程的角度来看,是同步还是异步调度都没有关系,但我们通常更喜欢异步调度。同样,为什么后台线程需要坐在那里等待UI更新完成。

  
      
  1. 是否只能在子线程上异步执行耗时的操作以保持UI完整?

    - (void)asyncChild {    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [NSThread sleepForTimeInterval:2]; // Simulated time-consuming operation
            dispatch_async(dispatch_get_main_queue(), ^{
                self.testImage=[UIImage imageNamed:xxx];
            });
        });
    }
    
  2.   

正确。并且我建议您使用第三种模式,并从代码中完全删除detachNewThreadSelector。坚持使用GCD。