Run Loops and the UIApplication subclass

What is a Run Loop?

A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. It's an abstraction that provides a mechanism to handle system input sources (sockets, ports, files, keyboard, mouse, timers, etc.)

Anatomy of a run loop, source: Apple

Each thread has its own run loop, which can be accessed via the currentRunLoop method. In general, you do not need to access the run loop directly, though there are some (networking) components that may allow you to specify which run loop they will use for I/O processing.

A run loop for a given thread will wait until one or more of its input sources has some data or event, then fire the appropriate input handler(s) to process each input source that is "ready.". After doing so, it will then return to its loop, processing input from various sources, and "sleeping" if there is no work to do.

The default run loop for the main thread is called the Main Run Loop.

There are the major components of a run loop:

  • Input sources - Deliver events asynchronously
    • Port-based sources - monitor the application's Mach ports
    • Custom input sources - monitors custom source of events
    • Cocoa perform selector sources - cocoa defines a custom input source that allows us to perform a selector on any thread
  • Timer sources -  Deliver events synchronously
    • Timers are a way to tell the thread to do something at a preset time in the future
  • Run loop observers - Run loop notifies the observer when the following events occur
    • The entrance to the run loop.
    • When the run loop is about to process a timer.
    • When the run loop is about to process an input source.
    • When the run loop is about to go to sleep.
    • When the run loop has woken up, but before it has processed the event that woke it up.
    • The exit from the run loop.

The sequence of events for a run loop 

Each time you run it, your thread’s run loop processes pending events and generates notifications for any attached observers. The order in which it does this is very specific and is as follows:

  1. Notify observers that the run loop has been entered.
  2. Notify observers that any ready timers are about to fire.
  3. Notify observers that any input sources that are not port based are about to fire.
  4. Fire any non-port-based input sources that are ready to fire.
  5. If a port-based input source is ready and waiting to fire, process the event immediately. Go to step 9.
  6. Notify observers that the thread is about to sleep.
  7. Put the thread to sleep until one of the following events occurs:
    1. An event arrives for a port-based input source.
    2. A timer fires.
    3. The timeout value set for the run loop expires.
    4. The run loop is explicitly woken up.
  8. Notify observers that the thread just woke up.
  9. Process the pending event.
    1. If a user-defined timer fired, process the timer event and restart the loop. Go to step 2.
    2. If an input source fired, deliver the event.
    3. If the run loop was explicitly woken up but has not yet timed out, restart the loop. Go to step 2.
  10. Notify observers that the run loop has exited.

Enough theory? Let's see some events in action.

I have created an Xcode project and create a new Swift file called main.swift to define a custom UIApplicationMain class.

[NOTE] - If you are creating a main.swft by yourself, don't forget to comment @UIApplicationMain annotation on the top of the AppDelegate class.

Notice something unusual here? In the third argument, we have give a class named "CustomApplication". Let's see what this class contains.

In this class, we have overridden a method called sendEvent where we can detect all the events triggered in the current run loop.

Let's go ahead, run the project, and tap on the screen to see what happens.

Perfect, we can see the taps in the console. It gives us all the information about the event that occurred for that particular touch in the main run loop.

The only time we need to run a run loop explicitly is when we create secondary threads for our application. The run loop for our application’s main thread is a crucial piece of infrastructure. As a result, the app frameworks provide the code for running the main application loop and start that loop automatically. 

The run method of UIApplication in iOS starts an application’s main loop as part of the normal startup sequence. If you use the Xcode template projects to create your application, you should never have to call these routines explicitly.

Post a Comment