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

How to sell popular products in the era of private domain traffic?

What is a hot product? Not only do we need to mak...

8 steps to teach you how to build your own closed loop of community monetization

Since 2020, many new brands and companies have be...

How to send messages in WeChat Enterprise Account Development

Companies can proactively send messages to employ...

Xiaohongshu bets on private domain traffic

In 2021, Xiaohongshu has been very active. Not on...

No traffic in the live broadcast room? 6 ways to increase your traffic!

Why is there no traffic in your live broadcast ro...

How do fish avoid danger? What are some ways to defend against enemies?

Produced by: Science Popularization China Author:...

The most comprehensive guide to event operations from entry to advanced!

Let’s start today’s official content! 1. Activity...

How can product operations keep users spending money?

The idea of ​​increasing order volume is shown in...