IOS multi threading – a thorough understanding of multi-threaded RunLoop

RunLoop

RunLoop 1.1
is what RunLoop?
1.2 RunLoop and 1.3 RunLoop RunLoop thread
default principle related
case of the main thread 2.1 CFRunLoopRef
2.2 CFRunLoopModeRef
2.3 CFRunLoopTimerRef
2.4 CFRunLoopSourceRef
2.5 CFRunLoopObserverRef RunLoop 4.1 NSTimer
RunLoop principle application using
4.2 ImageView
4.3 delayed display background thread (very common) Demo permanent address in this paper: YSC-RunLoopDemo

1 RunLoop profile

1.1 what is RunLoop?

Run represents a run, Loop represents a loop. To combine is to run the cycle. Haha, I prefer to “run”. As intuitive understanding is constantly running.

RunLoop is actually an object, the object for every event handler in the operation process in the cycle (such as touch events, UI refresh events, timer events, Selector events), continuous operation so as to maintain the program; and where there is no reason when the event will enter sleep mode to save CPU resources to improve the performance of the program.

1.2 RunLoop and thread

RunLoop and the thread is closely linked, we know that the thread is used to perform one or more specific tasks, but by default, after the thread will quit, it cannot perform the task. At this point we need to use a way to allow the thread to handle the task, do not exit. So, we have RunLoop.

  1. A thread corresponds to a RunLoop object, each thread has a unique corresponding RunLoop object.
  2. We can only manipulate the RunLoop of the current thread in the current thread, but not the RunLoop of other threads.
  3. The RunLoop object is created at the first time the RunLoop is acquired, and the destruction is done at the end of the thread.
  4. The main thread of the RunLoop object system automatically helps us to create it well (as follows), while the child thread of the RunLoop object requires us to create.

1.3 by default the main thread of the RunLoop principle

When we start a iOS program, the system will call the project automatically generated main.m file. The main.m file is as follows:

Int main (int argc, char * argv[]) {@autoreleasepool {return (argc, argv, nil, NSStringFromClass ([AppDelegate class]));}}

UIApplicationMain function which helps us to open the main thread of the RunLoop, UIApplicationMain has a wireless internal code. Above the code to open the RunLoop process can be simply understood as follows:

Int main (int argc, char * argv[] BOOL) {running = YES; do {/ / perform various tasks, handle various events / /... While...} (running); return 0;}

As can be seen from the above, the program has been in the DO-WHILE loop to perform, so the UIApplicationMain function has not been returned, we will not immediately exit the program after running the program, will remain in operation.

The following figure is given by Apple’s official RunLoop model.

IOS multi threading - a thorough understanding of multi-threaded RunLoop
official RunLoop model diagram

Can be seen from the diagram, a RunLoop cycle is in the thread, RunLoop will continue in the circulation, by Input sources (input source) and Timer sources (Ding Shiyuan) two sources of waiting for the event; then the received event notification thread processing, and the rest are in no event.

2 RunLoop related classes

Below we look at the Core Foundation framework on the RunLoop of the 5 categories, only to understand the meaning of these categories, we can understand the operating mechanism of RunLoop.

  1. CFRunLoopRef: object representing RunLoop
  2. CFRunLoopModeRef:RunLoop operation mode
  3. CFRunLoopSourceRef: the source / event source referred to in the RunLoop model diagram
  4. CFRunLoopTimerRef: is the timing source referred to in the RunLoop model diagram
  5. CFRunLoopObserverRef: observer, able to monitor the status of RunLoop changes

The following detailed explanation of the specific meaning and relationship of the following categories.

Look at a diagram of the 5 classes (source: http://blog.ibireme.com/2015/05/18/runloop/).

IOS multi threading - a thorough understanding of multi-threaded RunLoop
RunLoop related class diagram.Png

Then to explain the 5 class relationship (source: http://blog.ibireme.com/2015/05/18/runloop/), this article summarizes the special good, for reference, interested friends can see, very well written.

A RunLoop object (CFRunLoopRef) contains several operating modes (CFRunLoopModeRef). And each operation mode also includes a number of input sources (CFRunLoopSourceRef), timing source (CFRunLoopTimerRef), observer (CFRunLoopObserverRef).

  • Each time the RunLoop is started, only one of the operating modes (CFRunLoopModeRef) can be specified, which is called CurrentMode (CFRunLoopModeRef).
  • If you need to switch the operating mode (CFRunLoopModeRef), can only exit Loop, and then re designated a running mode (CFRunLoopModeRef) into.
  • This is done in order to separate the different groups of input source (CFRunLoopSourceRef), timing source (CFRunLoopTimerRef), observer (CFRunLoopObserverRef), so that they do not affect each other.

Below we explain in detail the following five categories:

2.1 CFRunLoopRef

CFRunLoopRef is the Core Foundation framework under the RunLoop object class. We can obtain the RunLoop object in the following ways:

  • Core Foundation (CFRunLoopGetCurrent); / / RunLoop object CFRunLoopGetMain to obtain the current thread (); / / get the main thread of the RunLoop object

Of course, the way to get the RunLoop object class in the Foundation framework is as follows:

  • Foundation [NSRunLoop currentRunLoop]; / / get the thread RunLoop [NSRunLoop mainRunLoop]; / / get the main thread of the RunLoop object

2.2 CFRunLoopModeRef

The system default defines a variety of operating modes (CFRunLoopModeRef), as follows:

  1. KCFRunLoopDefaultMode:App is the default mode of operation, usually the main thread is running in this mode
  2. UITrackingRunLoopMode: tracking user interaction events (for ScrollView tracking touch slide, to ensure that the interface is not affected by other Mode sliding)
  3. UIInitializationRunLoopMode: just started when the first entry of the App Mode, after the start is no longer used
  4. GSEventReceiveRunLoopMode: accept system internal events, usually not used
  5. KCFRunLoopCommonModes: pseudo mode, not a true mode of operation (used later)

The kCFRunLoopDefaultMode, UITrackingRunLoopMode and kCFRunLoopCommonModes are used in the development of our model, we use the specific method in 2.3 CFRunLoopTimerRef combined with CFRunLoopTimerRef to demonstrate.

2.3 CFRunLoopTimerRef

CFRunLoopTimerRef is a timing source (referred to in the RunLoop model diagram), understood as a trigger based on time, is basically NSTimer (ha ha, this understanding is simple).

Here we demonstrate the use of the combination of CFRunLoopModeRef and CFRunLoopTimerRef, so as to deepen understanding.

  1. First, we create a new iOS project, drag a Text Main.storyboard in View.
  2. In the ViewController.m file to add the following code, Demo call [self ShowDemo1]. – (void) viewDidLoad {[super viewDidLoad]; / / define a timer, two seconds after run agreed to call the self method of NSTimer *timer [NSTimer timerWithTimeInterval:2.0 target:self = selector:@selector (run) userInfo:nil repeats:YES]; / / the timer is added to the current RunLoop NSDefaultRunLoopMode [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];} – {(void) run NSLog (@ —run “);}
  3. Then run, at this time we found that if we do not do any operation on the simulator, the timer will be stable every 2 seconds to call the run method printing.
  4. But when we drag the Text View scroll, we find that the run method does not print, that is, NSTimer does not work. And when we let go of the mouse, NSTimer started working again.

This is because:

  • When we don’t do anything, RunLoop is under NSDefaultRunLoopMode.
  • And when we drag the Text View, RunLoop over NSDefaultRunLoopMode, switch to UITrackingRunLoopMode mode, this mode did not add NSTimer NSTimer, so we do not work.
  • But when we release the mouse, RunLoop will end the UITrackingRunLoopMode mode, and then switch back to the NSDefaultRunLoopMode mode, so NSTimer began to work again.

You can try the above code in the [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode] statement for the [[NSRunLoop, that is; the timer is added to the current RunLoop UITrackingRunLoopMode, you will find the timer will only work in drag the Text View mode, and do the operation when the timer is not working.

Can’t we get NSTimer to work properly in these two modes?

Of course, this is the use of the pseudo model we said before (kCFRunLoopCommonModes), this is not a real mode, but a tag mode means that can run in the play Common Modes marker mode.

So what patterns are marked on the Common Modes?

NSDefaultRunLoopMode and UITrackingRunLoopMode.

So we as long as we add NSTimer to the current RunLoop kCFRunLoopCommonModes (under the framework of Foundation NSRunLoopCommonModes), we can make NSTimer two cases in operation and Text View drag pleasant work.

Specific approach is to add statements to [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

Since talked about the NSTimer, here by way of the scheduledTimerWithTimeInterval method and the relationship between the RunLoop NSTimer. Add the following code:

[NSTimer scheduledTimerWithTimeInterval:2.0 selector:@selector (run) userInfo:nil target:self repeats:YES];

This code calls the scheduledTimer returned timer, NSTimer will automatically be added to the RunLoop NSDefaultRunLoopMode mode. This code is equivalent to the following two code:

NSTimer *timer = [NSTimer target:self selector:@selector (run) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode] (timerWithTimeInterval:2.0);

2.4 CFRunLoopSourceRef

CFRunLoopSourceRef is an event source (referred to in the RunLoop model diagram), CFRunLoopSourceRef has two classification methods.

  • The first one is categorized according to official documents (as in the RunLoop model): Port-Based Sources (port based) Custom Sources (custom) Cocoa Perform Selector Sources
  • Second according to the function call stack to classify: Source0: not based on Port Source1: Based on Port, through the kernel and other threads to communicate, receive, distribute system events

There is no difference between these two categories, but the first one is to classify the official theory, the second is in the practical application by calling the function to classify.

Let’s take a look at the function call stack and Source.

  1. Add a Button button to the Main.storyboard in our project and add a click action.
  2. Then click on the action code to add an output statement, and hit a breakpoint, as shown below: IOS multi threading - a thorough understanding of multi-threaded RunLoop
    add Button.png
  3. Then run the program and click the button.
  4. Then click the red part of the following figure in the project. IOS multi threading - a thorough understanding of multi-threaded RunLoop
    function call stack display
  5. You can see that as shown below is the function of the event call stack. IOS multi threading - a thorough understanding of multi-threaded RunLoop
    function call stack

So click on the event:

  1. First of all, the program starts, calling the 16 line of the main function, the main function calls the UIApplicationMain function of the 15 line, and then has been called up the function, the final call to the 0 line of the function, that is, click function.
  2. At the same time we can see that there are 11 lines of Sources0, that is, we click on the event is a Sources0 function, click the event is handled in the Sources0.
  3. As for Sources1, it is used to receive and distribute system events, and then distributed to the Sources0.

2.5 CFRunLoopObserverRef

CFRunLoopObserverRef is an observer, used to monitor the status of RunLoop changes

CFRunLoopObserverRef can monitor the status of the following changes:

Typedef CF_OPTIONS (CFOptionFlags, CFRunLoopActivity) {kCFRunLoopEntry (1UL = < < 0), is about to enter the Loop:1 kCFRunLoopBeforeTimers / / (1UL = < < 1), is Timer:2 kCFRunLoopBeforeSources = / / (1UL < < 2), is Source:4 kCFRunLoopBeforeWaiting = / / (1UL < < 5). About to enter dormancy: 32 / / kCFRunLoopAfterWaiting = (1UL < < 6), / / will wake up from sleep: 64 kCFRunLoopExit (1UL = < < 7), / / will exit from Loop: kCFRunLoopAllActivities = 0x0FFFFFFFU / 128} listening to all state changes;

Below we listen to the state changes in the RunLoop under the code.

  1. In the ViewController.m to add the following code, Demo call [self showDemo2]. – (void) viewDidLoad {[super viewDidLoad]; / / create observer CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler (CFAllocatorGetDefault), kCFRunLoopAllActivities (0, YES, (CFRunLoopObserverRef, observer, CFRunLoopActivity ^ activity) {NSLog (@ “listening to the change in RunLoop —%zd, activity);}); / / add to the current RunLoop CFRunLoopAddObserver observer ((CFRunLoopGetCurrent), observer, kCFRunLoopDefaultMode); / / observer release, finally finished adding need to release CFRelease (observer);}
  2. Then run, look at the print results, as shown below.
IOS multi threading - a thorough understanding of multi-threaded RunLoop
print results

You can see the status of RunLoop in the constant change, and finally became a state of 32, which is about to enter the sleep state, indicating that RunLoop will enter the sleep state.

3 RunLoop principle

Well, the five classes are explained, the following began to enlarge the recruit. So we can understand the logic of RunLoop.

Run a logic diagram below mentioned before the blogger provides (source: http://blog.ibireme.com/2015/05/18/runloop/)

IOS multi threading - a thorough understanding of multi-threaded RunLoop
RunLoop run logic diagram

This picture is very helpful for us to understand RunLoop, so we can talk about the RunLoop logic of the official document.

When the RunLoop is opened every time, the RunLoop of the thread will automatically process the previously untreated event, and notify the relevant observer.

The specific sequence is as follows:

  1. Notify observer RunLoop has started
  2. Notify the observer about to start the timer
  3. Notify the observer of any upcoming non port based source
  4. Start any ready – to – port source
  5. If the source based on the port is ready and is waiting for it, start immediately and go to step 9
  6. Notify the watcher thread to sleep
  7. Place the thread in the sleep to know any of the following events: an event arrives at the port based source timer to start the RunLoop setup time has been timed RunLoop is shown to wake up
  8. Notify the watcher thread to be awakened
  9. Handle an event that is not processed. If the user defined timer starts, the timer event is processed and the RunLoop is restarted. Step 2 if the input source is started, pass the appropriate message if the RunLoop is shown to wake up and the time has not timed out, restart the RunLoop. Step 2
  10. Notify observer RunLoop end.

4 RunLoop combat applications

Ha ha, the principle of talk so much from knowledge, and finally to the actual application link below.

It is no use to understand the light, it is hard to apply. Here are some of the application of RunLoop.

4.1 NSTimer use

NSTimer the use of methods in the CFRunLoopTimerRef class to explain in detail, the specific reference above 2.3 CFRunLoopTimerRef.

4.2 ImageView delayed display

Sometimes, we will encounter this situation:
when the interface contains UITableView, and each UITableViewCell inside have pictures. At this time when we roll UITableView, if there is a bunch of pictures need to be displayed, then there may be the phenomenon of Caton.

How to solve this problem?

At this time, we should postpone the display of the picture, that is, ImageView delayed display pictures. There are two ways:

1 listen to the UIScrollView scroll

Because UITableView inherited from UIScrollView, so we can listen to the UIScrollView scroll, UIScrollView related delegate can be achieved.

2 use PerformSelector to set the current thread RunLoop operation mode

Using performSelector method for UIImageView call setImage: method, and use inModes to set it to RunLoop under the NSDefaultRunLoopMode operating mode. The code is as follows:

[self.imageView performSelector:@selector (setImage:) withObject:[UIImage imageNamed:@ "Tupian" afterDelay:4.0 inModes:NSDefaultRunLoopMode];

The use of Demo under the demonstration of the method.

  1. In the project in the Main.storyboard to add a UIImageView, and add attributes, and simply add a constraint (or can not be displayed) as shown in the following figure. IOS multi threading - a thorough understanding of multi-threaded RunLoop
    add UIImageView
  2. Drag a picture into the project, such as the following. IOS multi threading - a thorough understanding of multi-threaded RunLoop
    tupian.jpg
  3. Then we add the following code in the touchesBegan method, in Demo, please call [self touchesBegan in the showDemo3] method. – (void) touchesBegan: (NSSet< UITouch *> touches withEvent: (* *) UIEvent event) {[self.imageView performSelector:@selector (setImage:) withObject:[UIImage imageNamed:@ afterDelay:4.0 inModes:@[NSDefaultRunLoopMode]] “Tupian”];}
  4. Run the program, click on the screen, then drag drag UIText View, more than 4 seconds, 4 seconds after the discovery, UIImageView also did not show the pictures, when we release, display pictures, results are as follows:
IOS multi threading - a thorough understanding of multi-threaded RunLoop
UIImageView delay display effect.Gif

In this way, we will achieve the drag after the delay in the display UIImageView.

4.3 background resident thread (very commonly used)

We are in the process of developing applications, especially if the background operation frequently, often do some time-consuming operation in the thread (download files, background music playback etc.), we can make the best of this thread is always in memory.

So how to do it?

Add a strong reference to the resident memory of Zi Xiancheng, in the thread under the RunLoop to add a Sources, open RunLoop.

The specific implementation process is as follows:

  1. In the project’s ViewController.m add a strong reference to the thread thread properties, as follows: IOS multi threading - a thorough understanding of multi-threaded RunLoop
    add thread attributes
  2. In viewDidLoad to create a thread self.thread, so that the thread starts and executes the Run1 method, the code is as follows. In Demo, please call [self showDemo4] at viewDidLoad; method. – (void) viewDidLoad {[super viewDidLoad]; / / create a thread, and call the Run1 method to perform the task of self.thread = [[NSThread alloc] initWithTarget:self selector:@selector object:nil] (Run1); / / thread [self.thread start] Run1 (void);} – {/ / written here task NSLog (@ “—-run1—–“); / / add two below the code, you can open RunLoop, after self.thread became the resident threads, can be added at any time, and handed over to the RunLoop currentRunLoop] addPort:[NSPort port] forMode: NSDefaultRunLoopMode] [[NSRunLoop; [[NSRunLoop currentRunLoop] run]; / / test whether to open the RunLoop, if RunLoop is open, can not come here, because the RunLoop open cycle. NSLog (@ “RunLoop”);}
  3. After the operation was found to print the —-run1—–, and did not open the RunLoop is not printed.

At this time, we will open a resident thread, below we try to add other tasks, in addition to the time before the creation of the call to the Run1 method, we also click on the time to call the run2 method.

So, we call PerformSelector in touchesBegan, so as to achieve the time to click on the screen to call the run2 method. Demo address. Specific code is as follows:

- (void) touchesBegan: (NSSet< UITouch *> touches withEvent: (* *) UIEvent event) {/ / by performSelector, in the self.thread thread to call the run2 method to perform the task [self performSelector:@selector (run2) onThread:self.thread withObject: nil waitUntilDone:NO];} - {(void) run2 NSLog (@ ----run2------);}

After running the test, in addition to the previously printed —-run1—–, whenever we click on the screen, you can call —-run2——.
so that we can achieve the requirements of the resident thread.


Learn more about multi thread series:

  • IOS multi threading – a thorough understanding of multi-threaded pthread, NSThread
  • IOS multi threading – a thorough understanding of multi-threaded GCD
  • IOS multi threading – a thorough understanding of multi-threaded NSOperation