The summary of Runloop
A RunLoop is a thread-dependent event handling loop where we can schedule operations and coordinate the reception of events that are passed in from outside. The purpose of RunLoop is to make our threads work when there is work to do and sleep when there is no work to do. So RunLoop keeps the program alive and hibernates when there are no messages to process to save CPU resources.
Details of the Runloop
What Runloop does:
The “end” at this point will not be printed because UIApplicationMain(Argc, Argv, nil, appDelegateClassName) comes in after the program is started from main; You create a Runloop bound to the main thread. The existence of Runloop keeps the App alive, allowing it to be on standby to handle user actions and other events. Otherwise, the App will end as soon as it launches! This is a good description for a Runloop on the main thread, but it’s too narrow to say that a Runloop is bound to a thread. Runloop should be a way to ensure that the current thread’s life cycle is not terminated.
Runloop consists of:
We usually use NSRunloop is the encapsulation of CFRunLoopRef, providing an object-oriented API. From a development point of view, we should still go into CFRunLoopRef to investigate Runloop.
There are five classes for RunLoop in CoreFoundation: CFRunloopRef, CFRunLoopModeRef, CFRunLoopSourceRef, CFRunLoopTimerRef, CFRunLoopObserverRef. performNSLog(@"%@", [NSRunLoop currentRunLoop]);
Print thumbnail of:
One Runloop corresponds to multiple modes, and one mode corresponds to multiple sources, timers, and observers. RunLoop can only run on one mode at a time. If you need to switch mode, you can only exit currentMode and switch to the specified mode. The corresponding relationship is as follows:
CFRunLoopModeRef:
A RunloopMode is a collection of sources, timers, and observers, and mode filters out unwanted events.
When a RunLoop runs in one mode, it does not receive or process events in other modes. To keep a mode alive, you must add at least one source, timer, or observer to it.
There are two modes disclosed by Apple: kCFRunLoopDefaultMode and UITrackingRunLoopMode. ScrollerView and its subclasses use this mode for smooth scrolling. These two modes are executed independently of each other and do not interfere with each other. Having different modes do their job is a good way to decouple the program.
CFRunLoop has a pseudo mode called kCFRunLoopCommonModes, which is not a real mode, but a collection of modes. Whenever events are added to CommonModes, they are added to all modes in it.
CFRunLoopSourceRef:
Sources are event Input Sources. They are divided into three types: Port-based Sources, Custom Input Sources, and Cocoa Perform Selector Sources. In reality, there are only two event sources: source0 and Source1.
Source0 is an internal app messaging mechanism that calls CFRunLoopSourceSignal() to mark the source as unprocessed, and then uses CFRunLoopWakeUp() to wake up the RunLoop to process the event. Source1 is based on mach_ports and is used to send messages to each other through the kernel and other threads.
CFRunLoopTimerRef:
A timer is a timer that we’re familiar with in OC. We use NSTimer will have several attention points, usually with scheduledTimerWithTimeInterval initialized timer don’t have to manually add to the Runloop, The timer created with timerWithTimeInterval is manually added to the Runloop. And here’s a classic interview question:
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { Dispatch_async (dispatch_get_global_queue(0, 0), ^{NSLog(@" start "); [self performSelector:@selector(testFunc) withObject:nil afterDelay:0]; NSLog (@ "end"); }); } - (void)testFunc {NSLog(@" call "); }Copy the code
[self performSelector:@selector(testFunc) withObject:nil afterDelay:0]; Runloop is added to the current subthread, but is disabled by default for the subthread. So in [self performSelector:@selector(testFunc) withObject:nil afterDelay:0]; Add [[NSRunLoop currentRunLoop] run]; To start the Runloop for the child thread.
CFRunLoopObserverRef:
This observer can observe the seven states of a Runloop:
Here’s what I learned from testing the Demo:
The Runloop of the main thread enters the hibernated kCFRunLoopBeforeWaiting state after processing the events input by the timer, source and observer after being started. Until my self-written timer calls Runloop to the kCFRunLoopAfterWaiting state, each time after processing the callback of the timer testFunc, it enters the kCFRunLoopBeforeWaiting state, Until it is awakened by the next timer into kCFRunLoopAfterWaiting state. The loop continues until the timer ends and the Runloop goes to sleep.Copy the code
The relationship between Runloop and thread:
The main thread has a corresponding mainRunLoop, which can be obtained by [NSRunLoop mainRunLoop] or [NSRunLoop currentRunLoop] in the main thread. If a child thread does not actively or implicitly obtain the current thread’s Runloop in some way, then the thread does not have a Runloop. Apple has no way to create a runloop, but you can create a runloop on the current thread using currentRunloop. A RunLoop is a one-to-one correspondence with a thread. There can be a thread without a RunLoop, but there must be a thread corresponding to a RunLoop. A Runloop is placed in a global Map with the thread as the key.
The method is as follows:
CFRunLoopRefstatic CFMutableDictionaryRef __CFRunLoops = NULL; // Select * from dictionary where key is pthread_t and value is CFRunLoopRefstatic CFMutableDictionaryRef __CFRunLoops = NULL; CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {// Create global dictionary if (! __CFRunLoops) {/ / create a mutable dictionary CFMutableDictionaryRef dict = CFDictionaryCreateMutable (); // Create RunLoop CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); CFDictionarySetValue(dict, pthread_main_thread_np(), mainLoop); } loop CFRunLoopRef loop = CFDictionaryGetValue(__CFRunLoops, t);} loop cfrunloopLoop = CFDictionaryGetValue(__CFRunLoops, t); // If not, create a new one and store it in the if (! loop) { CFRunLoopRef newLoop = __CFRunLoopCreate(t); CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop); } return loop; } // Get RunLoop CFRunLoopRef CFRunLoopGetMain(void) {if (! __main) __main = _CFRunLoopGet0(pthread_main_thread_np()); return __main; }Copy the code
Runloop Runs the process: