I do not know if you have ever thought about this problem, an application will be placed there after the start of operation, if it does not do anything, the application is like a static, no spontaneous action occurs, but if we click a button on the interface, this time there will be a corresponding button response event occurs. It gives us the feeling that the application is always on standby, that it is resting when no one is working, and that it responds immediately when it is allowed to work. In fact, this is the credit of the run loop.
One, thread and run loop
1.1 Types of thread tasks
Then talk about threading. Some threads perform tasks that are a straight line, a starting point to an end point, while others work on a circle and loop until it terminates in some way. A straight thread, such as the simple Hello World, runs out of print, its life cycle ends, like a flash in the pan, and the circle type, like the operating system, runs until you turn it off. In iOS, a round thread is implemented through a loop of run loops.
1.2 The relationship between a thread and a run loop
Run loop, as its name implies, loops represent a loop that, together with run, represents a loop that has been running. In fact, the run loop and the thread are tightly connected, so you can say that the run loop is meant for threading and without threads, it doesn't have to exist. Run loops is the infrastructure part of the thread, and both cocoa and corefundation provide the run loop object for easy configuration and management of the thread's run loop (cocoa for example below). Each thread, including the main thread of the program, has a corresponding run loop object.
The run loop of the 1.2.1 main thread is started by default.
iOS application, the program starts with a following main () function:
int main (int argc,char *argv[])
{
@autoreleasepool {
Return Uiapplicationmain (argc, argv, Nil, Nsstringfromclass ([Appdelegate class]));
}
}
The focus is on the Uiapplicationmain () function, which sets a Nsrunloop object for the main thread, which explains why our application can rest in the absence of an operation and respond immediately when it needs to work.
1.2.2 to other threads, the run loop is not started by default, and if you need more thread interaction you can manually configure and start it if the thread is simply going to perform a long, determined task.
1.2.3 in any of the threads of a cocoa program, you can pass:
Nsrunloop *runloop = [Nsrunloopcurrentrunloop];
To get the run loop to the current thread.
1.3 A few notes on the run loop
The Nsrunloop class in 1.3.1 cocoa is not thread-safe
We can no longer manipulate the run loop object of another thread in one thread, which is likely to cause unintended consequences. Fortunately, the opaque class cfrunloopref in Corefundation is thread-safe, and both types of run loops can be used in a mixed manner. The Nsrunloop class in cocoa can be done by using an instance method:
-(CFRUNLOOPREF) Getcfrunloop;
Get the corresponding Cfrunloopref class for thread-safe purposes.
The management of the 1.3.2 Run loop is not entirely automatic.
We still have to design the thread code to start the run loop at the appropriate time and respond correctly to the input event, if the run loop is needed in the thread. Also, we need to use the WHILE/FOR statement to drive the run loop to work, and the following code successfully drives a run loop:
BOOL isrunning = NO;
do {
IsRunning = [[Nsrunloopcurrentrunloop] runmode:nsdefaultrunloopmodebeforedate:[nsdatedistantfuture];
} while (isrunning);
1.3.3 Run Loop is also responsible for the creation and release of Autorelease pool
In projects that use manual memory management, many of the objects that are automatically freed are often used, which can cause a sharp increase in memory consumption if they are not released immediately. The run loop does this for us, and whenever a run cycle ends, it releases the Autorelease pool, and all the auto-release type variables in the pool are freed.
Advantages of 1.3.4 Run loop
A run loop is an event-processing loop that continuously listens and processes input events and assigns them to the corresponding targets for processing. If you just want to implement this function, you might think of a simple while loop can not be implemented, the cost of the boss to make a complex mechanism? Obviously, Apple architects are not eating cooked rice, and you think of them long ago.
First of all, Nsrunloop is a more sophisticated message processing model, and he has better abstraction and encapsulation of the message processing process, so that you do not have to deal with some very trivial and low-level specific message processing, in Nsrunloop each message is packaged in the input source or timer source (see below).
Secondly, it is also important to use the run loop to enable your thread to work at work and hibernate when it is not working, which can greatly save system resources.
Second, Run loop related knowledge points
2.1 Input Event Source
The Run loop receives input events from two different sources: the input source and the timing feed (timer source). Both provenances use a specific processing routine of the program to handle the event that arrives. Figure 1 shows the conceptual structure of the run loop and the various sources.
It should be noted that when you create an input source, you need to assign it to one or more patterns in the run loop (what is the schema, as described below). The pattern only affects the source of the listener in a particular event. In most cases, run loop runs in the default mode, but you can also make it run in custom mode. If a source is not being monitored in the current mode, any messages generated by it will only be passed in its associated mode when run loop is running.
Figure 1 Runloop structure and input source type
2.1.1 Input source
Passes an asynchronous event, typically a message from another thread or program. The input source passes an asynchronous message to the appropriate processing routine, and calls the Rununtildate: method to exit (The associated Nsrunloop object call within the thread).
2.1.1.1 Port-based input source
Port-based input sources are sent automatically by the kernel.
The Cocoa and core foundation built-in supports port-based sources that are created using port-related objects and functions. For example, in cocoa you never need to create an input source directly. You simply create the Port object and use the Nsport method to add the port to the run loop. The Port object handles the creation and configuration of the input source itself.
In core Foundation, you must manually create the port and its run loop source. We can use port-related functions (CFMACHPORTREF,CFMESSAGEPORTREF,CFSOCKETREF) to create the appropriate object. The following example shows how to create a port-based input source, add it to the run loop, and start:
Voidcreateportsource ()
{
Cfmessageportref port = cfmessageportcreatelocal (Kcfallocatordefault,cfstr ("Com.someport"), MyCallbackFunc, NULL, NULL);
Cfrunloopsourceref Source = Cfmessageportcreaterunloopsource (Kcfallocatordefault, port,0);
Cfrunloopaddsource (Cfrunloopgetcurrent (), source,kcfrunloopcommonmodes);
while (pagestillloading) {
NSAutoreleasePool *pool = [[Nsautoreleasepoolalloc] init];
Cfrunlooprun ();
[Pool release];
}
Cfrunloopremovesource (Cfrunloopgetcurrent (), Source,kcfrunloopdefaultmode);
Cfrelease (source);
}
2.1.1.2 Custom Input sources
The custom input source needs to be sent manually from other threads.
In order to create a custom input source, you must use the Cfrunloopsourceref type-related function inside the core foundation to create it. You can use a callback function to configure a custom input source. Core Fundation calls the callback function at different places in the configuration source, processes the input event, and cleans it when the source is removed from the run loop.
In addition to defining the behavior of customizing the input source when an event arrives, you must also define a message passing mechanism. This part of the source runs in a separate thread and is responsible for passing the data to the source and notifying it to process the data while the data is waiting to be processed. The definition of a messaging mechanism depends on you, but it's best not to be overly complex. An example of creating and starting a custom input source is as follows:
Voidcreatecustomsource ()
{
Cfrunloopsourcecontext context = {0,null, null,null, Null,null, Null,null, null,null};
Cfrunloopsourceref Source =cfrunloopsourcecreate (kcfallocatordefault,0, &context);
Cfrunloopaddsource (Cfrunloopgetcurrent (), Source,kcfrunloopdefaultmode);
while (pagestillloading) {
NSAutoreleasePool *pool = [[Nsautoreleasepoolalloc] init];
Cfrunlooprun ();
[Pool release];
}
Cfrunloopremovesource (Cfrunloopgetcurrent (), Source,kcfrunloopdefaultmode);
Cfrelease (source);
}
Selector source on 2.1.1.3Cocoa
In addition to port-based sources, cocoa defines a custom input source that allows you to execute the Selector method on any thread. As with Port-based sources, executing selector requests is serialized on the target thread, slowing down many of the synchronization issues that are likely to be caused by multiple methods on the threads. Unlike port-based sources, a selector is automatically removed from the run loop when it finishes executing.
When executing selector on top of other threads, the target thread must have an active run loop. For the thread that you create, this means that the thread will not execute the Selector method until you explicitly start the run loop, but is dormant.
The NSObject class provides a selector method similar to the following:
-(void) Performselectoronmainthread: (SEL) Aselector withobject: (ID) Argwaituntildone: (BOOL) Wait modes: (Nsarray *) Array
2.1.2 Timing Sources (timer source)
Timed feeds deliver messages at a preset point in time, and these messages occur at a specific time or at a repeating interval. The timing source passes the message directly to the processing routine and does not immediately exit the run loop.
It is important to note that although timers can produce time-based notifications, it is not a real-time mechanism. As with the input source, the timer is also related to the specific mode of your run loop. If the mode in which the timer is located is not currently monitored by the run loop, the timer will not start until the run loop is running in the appropriate mode. Similarly, if the timer starts during run loop processing of an event, the timer waits until the next run loop starts the corresponding handler. If the run loop is no longer running, the timer will never start.
There are two ways of creating a timer source,
Method One:
Nstimer *timer = [Nstimer scheduledtimerwithtimeinterval:4.0
Target:self
Selector: @selector (backgroundthreadfire:) userinfo:nil
Repeats:yes];
[[Nsrunloop Currentrunloop] addTimer:timerforMode:NSDefaultRunLoopMode];
Method Two:
[Nstimer Scheduledtimerwithtimeinterval:10
Target:self
Selector: @selector (backgroundthreadfire:)
Userinfo:nil
Repeats:yes];
2.2 Runloop Observer
The source is triggered when an appropriate synchronous or asynchronous event occurs, while the run loop watcher is triggered at a specific time when the run loop itself is running. You can use the Run Loop Viewer to prepare for processing a particular event or entering a dormant thread. You can associate a run loop observer with the following event:
1. Runloop Entrance
2. Runloop when to process a timer
3. Runloop when to process an input source
4. When Runloop enters sleep state
5. When the Runloop is awakened, but the event to be processed before waking
6. Runloop Termination
Similar to timers, you can specify that the run loop observer can be used only once or in a loop when it is created. If it is used only once, it will remove itself from the run loop after it is started, and the observer of the loop will not. Define the observer and add it to the run loop, using only the core fundation. The following example shows how to create an observer for the run Loop:
-(void) Addobservertocurrentrunloop
{
The application uses garbage collection, so noautorelease pool is needed.
Nsrunloop*myrunloop = [Nsrunloop currentrunloop];
Create a run loop observer and attach it to the runloop.
Cfrunloopobservercontext context = {0,self, null,null, NULL};
Cfrunloopobserverref Observer =cfrunloopobservercreate (Kcfallocatordefault,
Kcfrunloopbeforetimers,yes, 0, &myrunloopobserver, &context);
if (Observer)
{
Cfrunloopref cfloop = [Myrunloopgetcfrunloop];
Cfrunloopaddobserver (CFLOOP, Observer, Kcfrunloopdefaultmode);
}
}
Among them, kcfrunloopbeforetimers means selecting the listener timer to trigger the pre-processing event, followed by Yes to indicate cyclic monitoring.
2.3 Runloop Event Queue
Each time the run loop runs, the run loop pair of your thread automatically processes the previously unhandled message and notifies the relevant observer. The specific order is as follows:
- Notifies the watcher that the run loop has started
- Notifies the observer of any timer that is about to start
- Notifies the observer of any upcoming non-port-based sources
- Start any prepared non-port-based source
- If the port-based source is ready and in a wait state, start immediately and go to step 9.
- Notifies the watcher thread to enter hibernation
- Put a thread into hibernation until either of the following events occurs:
- An event arrives at a port-based source
- Timer start
- Time for Run loop setting has timed out
- Run loop is explicitly awakened
- Notifies the Observer that the thread will be awakened.
- Handling Unhandled Events
- If the user-defined timer starts, the timer event is processed and the run loop is restarted. Go to step 2
- If the input source is started, the corresponding message is passed
- If the run loop is explicitly awakened and the time has not expired, restart the run loop. Go to step 2
- Notifies the Observer that run loop is finished.
Because the observer for the timer and input source is passing the message before the corresponding event occurs, there may be an error between the time of the notification and the time the actual event occurred. If you need precise time control, you can use hibernation and wake-up notifications to help you proofread the actual event time.
Because the timer and other recurring events often need to be passed when you run running loop, revoking the run loop also terminates message delivery. A typical example is mouse path tracking. Because your code obtains the message directly rather than passing it through the program, the active timer does not start until the mouse trace ends and gives control to the program.
The run loop can be explicitly awakened by the run loop object. Other messages can also wake up the run loop. For example, adding a new non-port-based source wakes up the run loop so that the input source can be processed immediately without waiting for other events to occur before processing.
From this event queue, you can see:
① If an event arrives, the message is passed to the appropriate handler for processing, and when runloop finishes processing the event, the run loop exits, regardless of the time previously scheduled. You can restart the run loop to wait for the next event.
② If there is a source in the thread that needs to be processed, but the responding event does not arrive, the thread sleeps and waits for the corresponding event to occur. This is why the run loop can be used to keep the thread busy while it is working, while it is dormant when it is not working.
2.4 When to use the run loop
You need to explicitly run a run loop only if you are creating a worker thread for your program. Run Loop is a key part of the program's main thread infrastructure. Therefore, the cocoa and carbon programs provide a loop for the code to run the main program and automatically start the run loop. The UIApplication run method in the iOS program (or nsapplication in Mac OS x) is part of the program startup step, which starts the program's main loop when the program starts normally. Similarly, the Runapplicationeventloop function starts the main loop for the carbon program. If you use the templates provided by Xcode to create your program, you never need to explicitly call these routines yourself.
For worker threads, you need to determine whether a run loop is required. If it is necessary, then you need to configure and start it yourself. You don't need to start a thread's run loop under any circumstances. For example, when you use a thread to handle a predefined long-running task, you should avoid starting the run loop. The Run loop is required when you want to have more interaction with the thread, such as the following:
- Use ports or custom input sources to communicate with other threads
- Using the thread's timer
- Use any performselector in cocoa ... The method
- Make threads work periodically
If you decide to use Run loop in your program, it's simple to configure and start. As with all threading programming, you need to plan the situation where the worker thread exits the thread. It is often better to let a thread quit naturally than to force it off.
Run Loop detailed