RunLoop Learning record
What is the RunLoop
Running cycle, keep the program continues to run, don’t quit, with events in the processing of APP runloop source download address: opensource.apple.com/tarballs/CF…
The basic function
- To keep the program running,
- Handle various events in APP (touch, timer,GCD asynchronous back to main thread, block callback in runloop, etc.)
- Save CPU resources, provide performance, work when you work, rest when you rest
RunLoop object
IOS provides two sets of apis to access Runlopp
- Fundation : NSRunLoop
[NSRunLoop currentRunLoop]; // Get the current thread's RunLoop object [NSRunLoop mainRunLoop]; // Get the main thread RunLoop objectCopy the code
- Core Fundation : CFRunloop
CFRunLoopGetCurrent(); // Get the current thread's RunLoop object CFRunLoopGetMain(); // Get the main thread RunLoop objectCopy the code
NSRunLoop is a layer of OC wrapper based on CFRunloop
RunLoop with thread
- Each thread has a unique RunLoop object corresponding to it
- Runloops are stored in a global Dictionary, with threads as keys and runloops as values
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
Copy the code
- The thread is created without a RunLoop object; the RunLoop is created the first time it gets it
- The RunLoop is destroyed at the end of the thread
Runloop-related classes
Five classes for RunLoop in Core Foundation
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopObserverRef
CFRunLoop structure
struct __CFRunLoop { pthread_t _pthread; CFMutableSetRef _commonModes; CFMutableSetRef _commonModes; CFMutableSetRef _commonModes; CFMutableSetRef _commonModeItems; //commonMode item set CFRunLoopModeRef _currentMode; CFMutableSetRef _modes; // Set of CFRunLoopModeRef type, relative to NSArray order, Set is unordered Set};Copy the code
CFRunLoopMode
- CFRunLoopModeRef indicates the running mode of RunLoop
- A RunLoop contains several Mode, each Mode and contains several Source0 / Source1 / Timer/Observer
- Only one of these modes can be selected as currentMode when RunLoop starts
- If you need to switch Mode, can only exit from the current Loop, choose a Mode to enter again, is to solve the different groups of Source0 / Source1 / Timer/Observer can be separated, each other
- If there is no any Source0 / Source1 / Timer Mode/Observer, RunLoop immediately exit
There are five known modes
- KCFRunLoopDefaultMode: The default Mode of the App in which the main thread normally runs
- UITrackingRunLoopMode: interface tracing Mode, used by ScrollView to track touch sliding, ensuring that the interface sliding is not affected by other modes
- UIInitializationRunLoopMode: in the first to enter the first Mode when just start the App, start after the completion of the will no longer be used
- GSEventReceiveRunLoopMode: accept system internal Mode of events, usually in less than
- Kcfrunloopcommonmode: This is a placeholder Mode, not a real Mode
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
};
Copy the code
Runloop runs the logic
- CFMutableSetRef _sources0;
- Touch events
- perform selectors
- CFMutableSetRef _sources1;
- Port-based communication between threads
- CFMutableArrayRef _observers;
- The listener
- Used to listen for runloop status changes
- CFMutableArrayRef _timers;
- Timer, NSTimer
Core Code Parsing (Simplified version)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { int32_t retVal = 0; Do {// notify Observers: they are about to handle timers __CFRunLoopDoObservers(RL, RLM, kCFRunLoopBeforeTimers); // notify Observers: about to deal with Sources __CFRunLoopDoObservers(Rl, RLM, kCFRunLoopBeforeSources); __CFRunLoopDoBlocks(rl, RLM); __CFRunLoopDoBlocks(rl, RLM); Sources0 Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, RLM, stopAfterHandle); if (sourceHandledThisLoop) { __CFRunLoopDoBlocks(rl, rlm); If (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) { goto handle_msg; } // Notify Observers: about to hibernate __CFRunLoopDoObservers(rl, RLM, kCFRunLoopBeforeWaiting); // Go to sleep and wait for other messages to wake __CFRunLoopSetSleeping(rl); __CFPortSetInsert(dispatchPort, waitSet); do { __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy); } while (1); // wake up __CFPortSetRemove(dispatchPort, waitSet); __CFRunLoopUnsetSleeping(rl); // notify Observers: __CFRunLoopDoObservers(rl, RLM, kCFRunLoopAfterWaiting) have been awakened; handle_msg:; If (modeQueuePort! = MACH_PORT_NULL && livePort == modeQueuePort) { __CFRunLoopDoTimers(rl, rlm, Mach_absolute_time ())}else if (livePort == dispatchPort) __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); } else {/ / be awakened source1 __CFRunLoopDoSource1 (rl, RLM, RLS) | | sourceHandledThisLoop; } // execute blocks __CFRunLoopDoBlocks(rl, RLM); / / according to the result of previous processing to determine the return value if (sourceHandledThisLoop && stopAfterHandle) {retVal = kCFRunLoopRunHandledSource; } else if (timeout_context->termTSR < mach_absolute_time()) { retVal = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(rl)) { __CFRunLoopUnsetStopped(rl); retVal = kCFRunLoopRunStopped; } else if (rlm->_stopped) rlm->_stopped = false; retVal = kCFRunLoopRunStopped; } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) retVal = kCFRunLoopRunFinished; } voucher_mach_msg_revert(voucherState); os_release(voucherCopy); } while (0 == retVal); if (timeout_timer) { dispatch_source_cancel(timeout_timer); dispatch_release(timeout_timer); } else { free(timeout_context); } return retVal; } SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {// notify Observers: enter loop __CFRunLoopDoObservers(rL, currentMode, kCFRunLoopEntry); / / core logic __CFRunLoopRun (rl, currentMode, seconds, returnAfterSourceHandled, previousMode); // notify Observers: exit loop __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); return result; } void CFRunLoopRun(void) { /* DOES CALLOUT */ int32_t result; Do {result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); CHECK_FOR_FORK(); } while (kCFRunLoopRunStopped ! = result && kCFRunLoopRunFinished ! = result); }Copy the code
The underlying logic flow chart
RunLoop application scenarios
- Control the thread lifecycle (thread survival)
- Fixed NSTimer stopping working when sliding
- Monitoring application lag
- Performance optimization
- Flash back processing