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

  1. Fundation : NSRunLoop
[NSRunLoop currentRunLoop]; // Get the current thread's RunLoop object [NSRunLoop mainRunLoop]; // Get the main thread RunLoop objectCopy the code
  1. 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
  1. KCFRunLoopDefaultMode: The default Mode of the App in which the main thread normally runs
  2. UITrackingRunLoopMode: interface tracing Mode, used by ScrollView to track touch sliding, ensuring that the interface sliding is not affected by other modes
  3. 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
  4. GSEventReceiveRunLoopMode: accept system internal Mode of events, usually in less than
  5. 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;
    1. Touch events
    2. perform selectors

  • CFMutableSetRef _sources1;  
    1. Port-based communication between threads
  • CFMutableArrayRef _observers;
    1. The listener
    2. Used to listen for runloop status changes

  • CFMutableArrayRef _timers;
    1. 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