Article source: blog.csdn.net/jjunjoe/art…

www.cnblogs.com/ziyouchutuw… \

Runloop:

Run Loops is part of the thread-related infrastructure. A run loop is an event processing loop that continuously dispatches work and processes input events.

The purpose of using run loop is to keep your thread busy when it is working and dormant when it is not.

Runloop can also respond to other input sources, such as buttons, gestures, etc. while the loop is in the loop.

 

The Run loop receives input events from two different sources:

Input source and timer source.

Input sources deliver asynchronous events, usually messages from other threads or programs. Types of input sources: Port-based input sources and custom input sources.

The timing source delivers synchronous events that occur at a specific time or at repeated time intervals.

 

The Run Loop pattern is a collection of all input and timing sources to monitor and Run loop registered observers to notify.

Run Loop observers can be associated with the following events:

The Run loop entrance

Run loop When to process a timer

Run loop when to process an input source

Run loop When to enter the sleep state

When the Run loop is awakened, but the event to be processed before it is awakened

Run the loop to terminate

 

Each time you Run the Run loop, your thread’s Run Loop pair automatically processes the previously unprocessed message and notifies relevant observers. The specific order is as follows:

1. Notify the observer that Run loop has started.

2. Notify the observer of any timer that is about to start.

3. Notify the observer of any non-port-based sources that are about to start.

4. Start any prepared non-port-based sources.

5. If the port-based source is ready and in the waiting state, start immediately. Go to Step 9.

6. Notify the observer that the thread is asleep.

7. Put the thread to sleep until one of the following events occurs:

An event reaches a port-based source;

Timer start;

The Run loop time has timed out.

Run loop is explicitly woken up.

8. Notify the observer that the thread will wake up.

9. Handle unhandled events

If the user-defined timer starts, process the timer event and restart the Run loop. Go to Step 2.

If the input source is started, the corresponding message is passed.

If Run loop is woken up explicitly and the time has not timed out, restart the Run loop and go to Step 2.

10. Notify the observer that Run loop ends.

 

Run loop is only needed when you want more interaction with the thread, as in the following case:

Use ports or custom input sources to communicate with other threads;

Timers that use threads;

Any performSelector you use in Cocoa… The method;

Make threads work periodically.

 

2. Illustrate the advantages of Runloop.

In general, when we use NSRunLoop, the code looks like this:

do {

    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopModebeforeDate:[NSDate distantFuture]];

} while (! done);

In the above code, when done is NO, the current runloop will continue processing other input sources, and then return to the Runloop to wait for other input sources. The current flow stays in runloop until done is NO.

Is shown in the following code snippet is shown above, there are three buttons, corresponding to the following three action messages, buttonNormalThreadTestPressed, buttonRunloopPressed, buttonTestPressed.

ButtonNormalThreadTestPressed: start a thread, in the while loop waiting thread after running down then.

ButtonRunloopPressed: Start a thread, use runloop, and wait for the thread to finish before continuing.

ButtonTestPressed: Just print two logs to see if the UI responds immediately.

In this test, after the program runs, do the following operations:

1, click buttonNormalThreadTestPressed, and then click buttonTestPressed immediately, check the log output.

Click buttonRunloopPressed and then click buttonTestPressed immediately to check the log output and compare it to the log output of step 2. Even though the thread is not finished, the interface is still responding while the runloop waits.

 

BOOL threadProcess1Finished =NO;

-(void)threadProce1{

    NSLog(@”Enter threadProce1.”);

   

for (int i=0; i<5; i++) {

        NSLog(@”InthreadProce1 count = %d.”, i);

        sleep(1);

    }   

    threadProcess1Finished =YES;    

    NSLog(@”Exit threadProce1.”);

}

 

BOOL threadProcess2Finished =NO;

-(void)threadProce2{

    NSLog(@”Enter threadProce2.”);

   

for (int i=0; i<5; i++) {

        NSLog(@”InthreadProce2 count = %d.”, i);

        sleep(1);

    }

   

    threadProcess2Finished =YES;   

    NSLog(@”Exit threadProce2.”);

}

 

– (IBAction)buttonNormalThreadTestPressed:(UIButton *)sender {

    NSLog(@”EnterbuttonNormalThreadTestPressed”);

   

    threadProcess1Finished =NO;

    NSLog(@”Start a new thread.”);

    [NSThreaddetachNewThreadSelector: @selector(threadProce1)

                             toTarget: self

                           withObject: nil];

   

The usual code for waiting for a thread to finish processing before continuing is as follows.

// Call buttonTestPressed while waiting for the thread threadProce1 to complete. The interface does not respond until threadProce1 runs and the logs inside buttonTestPressed are printed.

while (! threadProcess1Finished) {

        [NSThreadsleepForTimeInterval: 0.5];

    }

   

    NSLog(@”ExitbuttonNormalThreadTestPressed”);   

}

 

– (IBAction)buttonRunloopPressed:(id)sender {

    NSLog(@”Enter buttonRunloopPressed”);

    threadProcess2Finished =NO;

    NSLog(@”Start a new thread.”);

    [NSThreaddetachNewThreadSelector: @selector(threadProce2)

                             toTarget: self

                           withObject: nil];

   

// With runloop, the situation is different.

Before waiting for thread Thread2 to complete, call buttonTestPressed. The interface responds and prints the log inside buttonTestPressed.

// This is the magic of runloop

while (! threadProcess2Finished) {

        NSLog(@”Begin runloop”);

        [[NSRunLoopcurrentRunLoop] runMode:NSDefaultRunLoopMode

                                 beforeDate: [NSDate distantFuture]];

        NSLog(@”End runloop.”);

    }

    NSLog(@”Exit buttonRunloopPressed”);

}

 

– (IBAction)buttonTestPressed:(id)sender{

    NSLog(@”Enter buttonTestPressed”);

    NSLog(@”Exit buttonTestPressed”);

}

 

The log information is as follows:

The 2013-04-07 14:25:22. 829 Runloop EnterbuttonNormalThreadTestPressed [657-11303]

2013-04-07 14:25:22.830 Runloop[657:11303] Start a new thread.

2013-04-07 14:25:22.831 Runloop[657:1250f] Enter threadProce1.

2013-04-07 14:25:22.832 Runloop[657:1250f] In threadProce1 count = 0.

2013-04-07 14:25:23.833 Runloop[657:1250f] In threadProce1 count = 1.

2013-04-07 14:25:24.834 Runloop[657:1250f] In threadProce1 count = 2.

2013-04-07 14:25:25.835 Runloop[657:1250f] In threadProce1 count = 3.

2013-04-07 14:25:26.837 Runloop[657:1250f] In threadProce1 count = 4

2013-04-07 14:25:27.839 Runloop[657:1250f] Exit threadProce1

The 2013-04-07 14:25:27. 840 Runloop ExitbuttonNormalThreadTestPressed [657-11303]

The 2013-04-07 14:25:27. 841 Runloop Enter buttonTestPressed [657-11303]

2013-04-07 14:25:27.842 Runloop[657:11303] Exit buttonTestPressed

2013-04-07 14:25:27.843 Runloop[657:11303] Enter buttonTestPressed

2013-04-07 14:25:27.844 Runloop[657:11303] Exit buttonTestPressed


2013-04-07 14:43:41.790 Runloop[657:11303] Enter buttonRunloopPressed

2013-04-07 14:43:41.790 Runloop[657:11303] Start a new thread.

2013-04-07 14:43:41.791 Runloop[657:11303] Begin runloop

2013-04-07 14:43:41.791 Runloop[657:14f0b] Enter threadProce2.

2013-04-07 14:43:41.792 Runloop[657:14f0b] In threadProce2 count = 0.

2013-04-07 14:43:42.542 Runloop[657:11303] End Runloop

2013-04-07 14:43:42.543 Runloop[657:11303] Begin runloop

The 2013-04-07 14:43:42. 694 Runloop Enter buttonTestPressed [657-11303]

The 2013-04-07 14:43:42. 694 Runloop [657-11303] Exit buttonTestPressed

2013-04-07 14:43:42.695 Runloop[657:11303] End Runloop

2013-04-07 14:43:42.696 Runloop[657:11303] Begin runloop

2013-04-07 14:43:42.793 Runloop[657:14f0b] In threadProce2 count = 1.

2013-04-07 14:43:43.326 Runloop[657:11303] End runloop.

2013-04-07 14:43:43.327 Runloop[657:11303] Begin runloop

The 2013-04-07 14:43:43. 438 Runloop Enter buttonTestPressed [657-11303]

The 2013-04-07 14:43:43. 438 Runloop [657-11303] Exit buttonTestPressed

2013-04-07 14:43:43.439 Runloop[657:11303] End Runloop

2013-04-07 14:43:43.440 Runloop[657:11303] Begin runloop

2013-04-07 14:43:43.795 Runloop[657:14f0b] In threadProce2 count = 2.

2013-04-07 14:43:44.797 Runloop[657:14f0b] In threadProce2 count = 3.

2013-04-07 14:43:45.798 Runloop[657:14f0b] In threadProce2 count = 4.

2013-04-07 14:43:46.800 Runloop[657:14f0b] Exit threadProce2.

 

Runloop:

– (void)viewDidLoad

{

    [super viewDidLoad];

    // Doany additional setup after loading the view, typically from a nib.

   

    [NSThread detachNewThreadSelector: @selector(newThreadProcess)

                             toTarget: self

                           withObject: nil];

}

 

– (void)newThreadProcess

{

    @autoreleasepool {

// Get the Runloop of the current thread

        NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];

     

// Set the environment for the Run Loop Observer

        CFRunLoopObserverContext context = {0,self,NULL,NULL,NULL};

       

// Create a Run Loop Observer

// The first argument is used to allocate memory for the observer

// The second parameter sets the events that the observer is concerned with. See the comment in the myRunLoopObserver callback

// The third parameter identifies whether the Observer is executed on the first entry to runloop or on each entry to runloop processing

// The fourth parameter sets the priority of the Observer

// The fifth parameter sets the observer callback function

// The sixth parameter sets the operating environment of the Observer

        CFRunLoopObserverRef observer =CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);

        if(observer)

        {

// Convert the NSRunLoop type of Cocoa to the CFRunLoopRef type of CoreFoundation

            CFRunLoopRef cfRunLoop = [myRunLoop getCFRunLoop];

           

// Add the new observer to the runloop of the current thread

            CFRunLoopAddObserver(cfRunLoop, observer, kCFRunLoopDefaultMode);

        }

       

        //

        [NSTimer scheduledTimerWithTimeInterval: 1

                                        target: self

                                       selector:@selector(timerProcess)

                                      userInfo: nil

                                       repeats: YES];

        NSInteger loopCount = 2;

        do{

// Start the loop of the current thread until the specified time arrives. While the loop is running, the runloop processes all data from the InputSource associated with the runloop

// There is only one source of type Timer for the inputSource associated with the current run loop in this example.

// This Timer sends the trigger event to the Runloop every one second. When the Run loop detects this event, it calls the corresponding handler.

           

// Since an observer is added to the Run loop and the observer is set to be interested in all runloop behavior.

// When calling the runUnitDate method, the Observer detects that runloop has started and entered the loop. The Observer calls its callback function, passing the action kCFRunLoopEntry as the second argument.

// The Observer detects the other behavior of runloop and calls the callback as described above.

[myRunLoop runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 5.0]].

// Exit the current runloop when the runloop time reaches. The Observer also detects the runloop’s exit behavior and calls its callback function, passing the behavior as the second argument kCFRunLoopExit.

            loopCount–;

        }while (loopCount);

    }

}

 

void myRunLoopObserver(CFRunLoopObserverRef observer,CFRunLoopActivityactivity,void *info)

{

    switch (activity) { 

            //The entrance of the run loop, beforeentering the event processing loop.  

            //This activity occurs once for each callto CFRunLoopRun and CFRunLoopRunInMode 

        case kCFRunLoopEntry: 

            NSLog(@”run loop entry”); 

            break; 

            //Inside the event processing loop beforeany timers are processed 

        case kCFRunLoopBeforeTimers: 

            NSLog(@”run loop before timers”); 

            break;

            //Inside the event processing loop beforeany sources are processed 

        case kCFRunLoopBeforeSources: 

            NSLog(@”run loop before sources”); 

            break; 

            //Inside the event processing loop beforethe run loop sleeps, waiting for a source or timer to fire.  

            //This activity does not occur ifCFRunLoopRunInMode is called with a timeout of 0 seconds.  

            //It also does not occur in a particulariteration of the event processing loop if a version 0 source fires 

        case kCFRunLoopBeforeWaiting: 

            NSLog(@”run loop before waiting”); 

            break; 

            //Inside the event processing loop afterthe run loop wakes up, but before processing the event that woke it up.  

            //This activity occurs only if the run loopdid in fact go to sleep during the current loop 

        case kCFRunLoopAfterWaiting: 

            NSLog(@”run loop after waiting”); 

            break; 

            //The exit of the run loop, after exitingthe event processing loop.  

            //This activity occurs once for each callto CFRunLoopRun and CFRunLoopRunInMode 

        case kCFRunLoopExit: 

            NSLog(@”run loop exit”); 

            break; 

/ *

             A combination of all the precedingstages

             case kCFRunLoopAllActivities:

             break;

* /

        default: 

            break; 

    } 

}

 

– (void)timerProcess{

    for (int i=0; i<5; i++) {       

        NSLog(@”In timerProcess count = %d.”, i);

        sleep(1);

    }

}

 

The debugging information is as follows:

2012-12-18 09:51:14.174 Texta[645:14807] run loop entry

2012-12-18 09:51:14.175 Texta[645:14807] run loop before timers

[645:14807] Run loop before sources

Texta[645:14807] Run loop before waiting

Texta[645:14807] Run loop after waiting

Texta[645:14807] In timerProcess Count = 0.

Texta[645:14807] In timerProcess count = 1.

Texta[645:14807] In timerProcess Count = 2.

Texta[645:14807] In timerProcess Count = 3.

Texta[645:14807] In timerProcess Count = 4.

Texta[645:14807] Run loop exit

2012-12-18 09:51:20.189 Texta[645:14807] run loop entry

2012-12-18 09:51:20.190 Texta[645:14807] run loop before timers

[645:14807] Run loop before sources

Texta[645:14807] Run loop before waiting

Texta[645:14807] Run loop after waiting

Texta[645:14807] In timerProcess Count = 0.

Texta[645:14807] In timerProcess count = 1.

Texta[645:14807] In timerProcess Count = 2.

Texta[645:14807] In timerProcess Count = 3.

Texta[645:14807] In timerProcess Count = 4.

Texta[645:14807] Run loop exit

 

A Runloop can block a thread and wait for another thread to execute.

Such as:

BOOL StopFlag =NO;

– (void)viewDidLoad

{

    [super viewDidLoad];

     // Doany additional setup after loading the view, typically from a nib.

   

    StopFlag =NO;

    NSLog(@”Start a new thread.”);

    [NSThread detachNewThreadSelector: @selector(newThreadProc)

                             toTarget:self

                           withObject: nil];

while (! StopFlag) {

        NSLog(@”Beginrunloop”);

        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode

                                 beforeDate: [NSDate distantFuture]];

        NSLog(@”Endrunloop.”);

    }

   

    NSLog(@”OK”);

}

 

-(void)newThreadProc{

    NSLog(@”Enter newThreadProc.”);

   

    for (int i=0; i<10; i++) {

        NSLog(@”InnewThreadProc count = %d.”, i);

        sleep(1);

    }

   

    StopFlag =YES;

   

    NSLog(@”Exit newThreadProc.”);

}

}

The debugging information is as follows:

2012-12-18 08:50:34.220 Runloop[374:11303] Start a new thread.

2012-12-18 08:50:34.222 Runloop[374:11303] Begin runloop

2012-12-18 08:50:34.222 Runloop[374:14b03] Enter newThreadProc.

2012-12-18 08:50:34.223 Runloop[374:14b03] In newThreadProc count = 0.

2012-12-18 08:50:35.225 Runloop[374:14b03] In newThreadProc count = 1.

2012-12-18 08:50:36.228 Runloop[374:14b03] In newThreadProc count = 2.

2012-12-18 08:50:37.230 Runloop[374:14b03] In newThreadProc count = 3.

2012-12-18 08:50:38.233 Runloop[374:14b03] In newThreadProc count = 4.

2012-12-18 08:50:39.235 Runloop[374:14b03] In newThreadProc count = 5.

2012-12-18 08:50:40.237 Runloop[374:14b03] In newThreadProc count = 6.

2012-12-18 08:50:41.240 Runloop[374:14b03] In newThreadProc count = 7.

2012-12-18 08:50:42.242 Runloop[374:14b03] In newThreadProc count = 8.

2012-12-18 08:50:43.245 Runloop[374:14b03] In newThreadProc count = 9.

2012-12-18 08:50:44.247 Runloop[374:14b03] Exit newThreadProc.

2012-12-1808:51:00.000 Runloop[374:11303] End Runloop

The 2012-12-18 08:51:00. 001 Runloop [374-11303] OK

As you can see from the debug print, statements executed after the while loop are executed a long time later. Because, by changing the StopFlag variable, the runloop object does not know that the runloop is not awakened at this time. Some other event wakes up the main thread at some point, which ends the while loop, but the delay is always variable.

 

Modify the code slightly:

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode

                                 beforeDate: ** [NSDate dateWithTimeIntervalSinceNow: 1] **];

Shortening runloop sleep time seems to solve the above problem.

However, this can cause runloop to wake up frequently, which defeats the purpose of runloop. The purpose of runloop is to keep your thread busy when it is working and dormant when it is not.

 

Finally, look at the correct way to write:

BOOL StopFlag =NO;

– (void)viewDidLoad

{

    [super viewDidLoad];

     // Doany additional setup after loading the view, typically from a nib.

   

    StopFlag =NO;

    NSLog(@”Start a new thread.”);

    [NSThread detachNewThreadSelector: @selector(newThreadProc)

                             toTarget: self

                           withObject: nil];

while (! StopFlag) {

        NSLog(@”Beginrunloop”);

        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode

                                 beforeDate: [NSDatedistantFuture]];

        NSLog(@”Endrunloop.”);

    }

   

    NSLog(@”OK”);

}

 

-(void)newThreadProc{   

    NSLog(@”Enter newThreadProc.”);

   

    for (int i=0; i<10; i++) {

        NSLog(@”InnewThreadProc count = %d.”, i);

        sleep(1);

    }

    ** [self performSelectorOnMainThread: @selector(setEnd)**

withObject: nil

waitUntilDone: NO];

   

    NSLog(@”Exit newThreadProc.”);

}

-(void)setEnd{

StopFlag = YES;

}


The debugging information is as follows:

Runloop[410:11303] Start a new thread.

2012-12-18 09:05:17.163 Runloop[410:14a03] Enter newThreadProc.

2012-12-18 09:05:17.164 Runloop[410:14a03] In newThreadProc count = 0.

2012-12-18 09:05:17.165 Runloop[410:11303] Begin runloop

2012-12-18 09:05:18.166 Runloop[410:14a03] In newThreadProc count = 1.

2012-12-18 09:05:19.168 Runloop[410:14a03] In newThreadProc count = 2.

2012-12-18 09:05:20.171 Runloop[410:14a03] In newThreadProc count = 3.

2012-12-18 09:05:21.173 Runloop[410:14a03] In newThreadProc count = 4.

2012-12-18 09:05:22.175 Runloop[410:14a03] In newThreadProc count = 5.

2012-12-18 09:05:23.178 Runloop[410:14a03] In newThreadProc count = 6.

2012-12-18 09:05:24.180 Runloop[410:14a03] In newThreadProc count = 7

2012-12-18 09:05:25.182 Runloop[410:14a03] In newThreadProc count = 8.

2012-12-18 09:05:26.185 Runloop[410:14a03] In newThreadProc count = 9.

The 2012-12-18 09:05:27. 188 Runloop [410:14 a03] Exit newThreadProc.

2012-12-1809:05:27.188 Runloop[410:11303] End Runloop

The 2012-12-18 09:05:27. 189 Runloop [410-11303] OK

Instead of setting variables directly, send a message to the main thread to wake up runloop.

\