UI operations in the main thread are not absolutely safe

UI operations in the main thread are not absolutely safe

[[185244]]

When we first started learning iOS, we were told that UI operations must be performed on the main thread. This is because UIKit methods are not thread-safe, and ensuring thread safety requires a lot of overhead. So the question is, is it safe to perform UI operations in the main thread?

Obviously, the answer is no!

In Apple's MapKit framework, there is a method called addOverlay, which requires the code to be executed not only on the main thread but also on the main queue of GCD when it is implemented at the bottom layer. This is an extremely rare problem, but someone has already encountered this problem when using ReactiveCocoa and submitted an issue.

Apple's Developer Technology Support admitted that this is a bug. Regardless of whether this is a bug or a legacy design, or whether it is just a quibble, in order to avoid falling into the same trap again, I think it is necessary to analyze the cause of the problem and the solution.

GCD knowledge review

In GCD, the main queue can be obtained by using the dispatch_get_main_queue() function. Calling the dispatch_sync() method will synchronously submit the task to the specified queue.

Note the difference between queues and threads. There is no "ownership relationship" between them. When we submit a task synchronously, we first block the current queue, and then wait until the next runloop to execute the block in the appropriate thread.

Before executing a block, it will first look for a suitable thread to execute the block, and then block the thread until the block is executed. The rule for finding threads is: any block submitted to the main queue will be executed in the main thread. Under the premise of not violating this rule, the document also tells us that the system will automatically optimize and execute the block in the current thread as much as possible.

By the way, the sufficient condition for GCD deadlock is: "repeatedly submitting blocks to the current queue synchronously". In principle, the cause of the deadlock is that the submitted block blocks the queue, and after the queue is blocked, dispatch_sync() can never be completed. It can be seen that this has nothing to do with the thread where the code is located.

Another example can also prove this point. In the main thread, a block is synchronously dispatched to a serial queue. According to the principle of thread selection above, the block will be executed in the main thread, but it will not cause deadlock:

  1. dispatch_queue_t queue = dispatch_queue_create( "com.kt.deadlock" , nil);
  2. dispatch_sync(queue, ^{
  3. NSLog(@ "current thread = %@" , [NSThread currentThread]);
  4. });
  5. // Output:
  6. // current thread = {number = 1, name = main}

Cause Analysis

After all this talk, let's get back to the bug described earlier. Now we know that even if the code is executed in the main thread, it is likely not running in the main queue (and vice versa). If we call MapKit's addOverlay method in a subqueue, even if it is currently in the main thread, it will cause a bug because the underlying implementation of this method determines the main queue rather than the main thread.

Thinking further, sometimes in order to ensure that UI operations run on the main thread, if there is a function that can be used to create a new UILabel, in order to ensure thread safety, the code may be like this:

  1. - (UILabel *)labelWithText: (NSString *)text {
  2. __block UILabel *theLabel;
  3. if ([NSThread isMainThread]) {
  4. theLabel = [[UILabel alloc] init];
  5. [theLabel setText:text];
  6. }
  7. else {
  8. dispatch_sync(dispatch_get_main_queue(), ^{
  9. theLabel = [[UILabel alloc] init];
  10. [theLabel setText:text];
  11. });
  12. }
  13. return theLabel;
  14. }

Strictly speaking, this way of writing is not 100% safe, because we cannot know whether the relevant system methods have the above bugs.

Solution

Since the block submitted to the main queue must be run in the main thread, and thread switching in GCD is usually caused by specifying a queue, we can make a more rigorous judgment, that is, to judge whether it is in the main queue instead of whether it is in the main thread.

GCD does not provide an API to make corresponding judgments, but we can find another way to use the dispatch_queue_set_specific and dispatch_get_specific methods to mark the main queue:

  1. + (BOOL)isMainQueue {
  2. static const void* mainQueueKey = @ "mainQueue" ;
  3. static void* mainQueueContext = @ "mainQueue" ;
  4.   
  5. static dispatch_once_t onceToken;
  6. dispatch_once(&onceToken, ^{
  7. dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueContext, nil);
  8. });
  9.   
  10. return dispatch_get_specific(mainQueueKey) == mainQueueContext;
  11. }

Use the isMainQueue method instead of [NSThread isMainThread] to get better safety.

References

1.Community bug reports about MapKit

http://t.cn/RtxivSc

2.GCD's Main Queue vs Main Thread

http://t.cn/RthOawx

3. Similar pitfalls encountered in ReactiveCocoa

http://t.cn/RtxJFRX

4.Why can't we use a dispatch_sync on the current queue?

http://t.cn/RtxJgPi

<<:  RecyclerView implements sliding deletion and dragging functions

>>:  Android Studio template file group

Recommend

Double Eleven, e-commerce industry leverages marketing skills!

The National Day is coming soon. A group of marke...

Six intelligent prospects for the development of the Internet of Things

No one knows exactly how many things in the world...

Lao Duan said: How far is Huawei from television?

In the second half of last year, Huawei Honor'...

QQ space stretchable head

Source code introduction: Imitating QQ space with...

Gaining weight, fatigue, lethargy...may be caused by a lack of this vitamin

Audit expert: Wang Xuejiang Professor of Pathophy...

10 Observations on E-commerce Marketing in 2020

This article, which contains 10 observations for ...

Silverware is loved by many people, what is its charm?

In ancient my country, only prominent families su...

Deep sea warning! Let's talk about our "fear below the sea level"

The blue ocean makes people feel relaxed and happ...

Real case|Actual case of product data-based operation analysis!

This analysis report is a data analysis for a ver...

Learn Bazi from scratch (Basics)

Learning Bazi from scratch (Basic Edition) Resour...

iOS 9 will allow developers to create Safari ad blocking software

[[136409]] On June 11, digital publishers and onl...