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

Create a TikTok video promotion ad in 5 minutes!

Come and check out the latest TikTok Ads product ...

Always want to "stick it"? Most likely you have skin hunger syndrome

Why do I always want to stick close to others? So...

10 basic skills necessary for operating Douyin

Douyin became a huge hit like a bolt from the blu...

5 ways to promote your brand!

Brand promotion is not just a high-sounding conce...

A complete event planning framework!

As an APP operator, you often need to plan activi...

How to choose the type of large website server rental?

At present, more and more people are starting to ...

How to use Xcode's Targets to manage development and production builds

[[164788]] This post was contributed by Eugene Tr...

The top ten screen-sweeping cases in the first half of 2021!

The Harvard Business Review once pointed out: In ...

Business rules for operating e-commerce platforms!

In the future, life will become more and more dif...

6 practical cases of marketing and promotion of Bilibili!

Today, the fragmented society has led to a sharp ...

Who will get a share of the 100 billion yuan terminal subsidies in 2016?

Source: NetEase Technology Original title: "...