IOS RunLoop and iosrunloop

Source: Internet
Author: User

IOS RunLoop and iosrunloop

RunLoop is an important concept related to iOS threads. Both the main thread and sub-thread correspond to a RunLoop. Without RunLoop, the thread will be immediately recycled by the system.

This article mainly analyzes the source code of CFRunLoop and briefly describes the principle of CFRunLoop.

CFRunLoop is open source, Open Source Address: http://opensource.apple.com/tarballs/CF/

Let's take a look at the figure. This is the main thread's RunLoop call function:

Find the CFRunLoop source code:

void CFRunLoopRun(void) {    /* DOES CALLOUT */    int32_t result;    do {        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);        CHECK_FOR_FORK();    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);}

As you can see, the system creates a do while loop. When the status is stopped or finished, the loop is exited, The RunLoop ends, and the thread is recycled.

Note: 1.0e10 indicates that 1.0 is multiplied by the 10th power of 10. This parameter mainly specifies the RunLoop time. Passing this time indicates that the thread is resident.

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */    CHECK_FOR_FORK();    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;    __CFRunLoopLock(rl);    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {        Boolean did = false;        if (currentMode) __CFRunLoopModeUnlock(currentMode);        __CFRunLoopUnlock(rl);        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;    }    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);    CFRunLoopModeRef previousMode = rl->_currentMode;    rl->_currentMode = currentMode;    int32_t result = kCFRunLoopRunFinished;    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);    __CFRunLoopModeUnlock(currentMode);    __CFRunLoopPopPerRunData(rl, previousPerRun);    rl->_currentMode = previousMode;    __CFRunLoopUnlock(rl);    return result;}

Run :__ CFRunLoopFindMode to check whether the Mode exists.

View the code annotation:

/* Call with rl locked, returns mode locked */static CFRunLoopModeRef _ CFRunLoopFindMode (CFRunLoopRef rl, CFStringRef modeName, Boolean create) {CHECK_FOR_FORK (); Limit rlm; struct _ CFRunLoopMode srlm; memset (& srlm, 0, sizeof (srlm); _ CFRuntimeSetInstanceTypeIDAndIsa (& srlm, _ kCFRunLoopModeTypeID); srlm. _ name = modeName; rlm = (CFRunLoopModeRef) CFSetGetValue (rl-> _ modes, & srlm); if (NU LL! = Rlm) {// if yes, _ CFRunLoopModeLock (rlm); return rlm;} if (! Create) {// case 2 return NULL;} // case 3 rlm = (CFRunLoopModeRef) _ CFRuntimeCreateInstance (kCFAllocatorSystemDefault, _ blank, sizeof (struct _ CFRunLoopMode) -sizeof (CFRuntimeBase), NULL); if (NULL = rlm) {return NULL ;}.... // create a Mode and assign the initial value.
_ CFRunLoopMode

_ CFRunLoopMode I understand as a running type, which indicates the type of event that the current thread runs under and will be awakened. It is like setting a Timer in the program to run in DefaultMode. However, if you slide UIScrollview, the system will change the Mode of the current thread to UITrackingRunLoopMode, at this time, your Timer will not be called, because the current thread Mode is different from your Timer Mode. Of course, if you want to call Timer in any Mode, you need to set the Mode to CommonMode.

So why is the design like this? I understand that this design is more flexible. You can specify the Mode of the current RunLoop when the event needs to be called, as in the preceding example.

The system provides the following five types corresponding to _ CFRunLoopMode:

We often use NSDefaultRunLoopMode and nsunloopcommonmodes in the code. You can also use a custom Mode.

_ CFRunLoopMode

The _ CFRunLoopMode struct includes Source0, Source1, Observers, Timers, Ports, and so on.

Source0: Process internal App events, such as UIEvent and CFSocket, corresponding to the function (cfrunloop_is_calling_out_to_a_source0_0000m_function.

Source1: It is managed by RunLoop and kernel, and is driven by Mach port, such as CFMachPort and CFMessagePort. Corresponds to the function (cfrunloop_is_calling_out_to_a_source1_m_function.

Observers: Modifies the RunLoop status. Status:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {    kCFRunLoopEntry = (1UL << 0),    kCFRunLoopBeforeTimers = (1UL << 1),    kCFRunLoopBeforeSources = (1UL << 2),    kCFRunLoopBeforeWaiting = (1UL << 5),    kCFRunLoopAfterWaiting = (1UL << 6),    kCFRunLoopExit = (1UL << 7),    kCFRunLoopAllActivities = 0x0FFFFFFFU};

Timers: Enables the App to respond to NSTimers and delayed perform events, corresponding to the function (CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION. We can add the following code in viewDidLoad:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES];[timer fire];- (void)test{    NSLog(@"abc");}

You can obtain the following information:

Here we will briefly introduce the DoTimers and DoTimer functions. DoTimers is a for loop that calls DoTimer. Then call CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION. The code of this function is very simple, that is, call the callback of NSTimer or Perform.

Ports: Port is used for communication between processes or threads. It can be divided into CFMachPort, CFMessagePort, and CFSocketPort. For details, see this article.

Then let's look at the Code:

volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);

_ Per_run_data is used to describe the status of the current CFRunLoop. There are three states: initial status, wake, and stop. Note the volatile keyword, which indicates that the compiler should not optimize the variable and read it from the memory every time.

Next:

if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);

The notification observer starts to enter the RunLoop. This function is mainly implemented through (CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION.

Next we will enter the core function _ CFRunLoopRun. This function is complicated, and port-related functions are ignored.

dispatch_source_t timeout_timer = NULL;    struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));    if (seconds <= 0.0) { // instant timeout        seconds = 0.0;        timeout_context->termTSR = 0ULL;    } else if (seconds <= TIMER_INTERVAL_LIMIT) {        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT);        timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);        dispatch_retain(timeout_timer);        timeout_context->ds = timeout_timer;        timeout_context->rl = (CFRunLoopRef)CFRetain(rl);        timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);        dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context        dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);        dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);        uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);        dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);        dispatch_resume(timeout_timer);    } else { // infinite timeout        seconds = 9999999999.0;        timeout_context->termTSR = UINT64_MAX;    }

The logic is clear. GCD is used to create a scheduled task and _ CFRunLoopTimeOut is used to wake up the RunLoop within the specified time. Because DISPATCH_TIME_FOREVER is used, _ CFRunLoopTimeOut is called only once.

_ Blocks (rl, rlm); // execute blockBoolean sourceHandledThisLoop = _ blocks (rl, rlm, stopAfterHandle) in RunLoop; if (sourceHandledThisLoop) {_ CFRunLoopDoBlocks (rl, rlm);} // The Source0 event in the execution Mode, such as perform and uievent. // I Don't Know Why _ CFRunLoopDoBlocks needs to be executed twice.

There will be two subsequent changes in the status: kCFRunLoopBeforeWaiting and kCFRunLoopAfterWaiting. Then there will be port-related changes.

Last section:

If (sourceHandledThisLoop & stopAfterHandle) {// when you enter the loop, the parameter indicates that the event will be returned after processing. RetVal = kCFRunLoopRunHandledSource;} else if (timeout_context-> termTSR <mach_absolute_time () {// The timeout value beyond the input parameter tag is retVal = kCFRunLoopRunTimedOut ;} else if (_ CFRunLoopIsStopped (rl) {// The _ CFRunLoopUnsetStopped (rl); retVal = stopped;} else if (rlm-> _ stopped) is forcibly stopped by external callers) {rlm-> _ stopped = false; retVal = kCFRunLoopRunStopped;} else if (_ CFRunLoopModeIsEmpty (rl, rlm, previusmode )) {// source/timer/observer none have retVal = kCFRunLoopRunFinished ;}

In general, RunLoop is relatively basic but complex, and many questions are encountered during the reading of the source code. Some questions may need to be found in subsequent studies.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.