Question

I read this apple doc about runloop:

A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events... A run loop receives events from two different types of sources. Input sources deliver asynchronous events... Timer sources deliver synchronous events...

Now I know performSelector:withObject:afterDelay: and NSTimer run in a runloop.

The doc doesn't mention touch events as input sources. I wonder:

Q1: Do touch events sent by [UIApplication sendEvent:] run in some default runloop too?
Q2: If Q1's answer is YES, is that default runloop the same runloop handles performSelector:withObject:afterDelay: and NSTimer events?

Was it helpful?

Solution

I won't pretend on accurateness of my answer its only just my guesses.

What I will cover in this answer:

  1. Mach IPC/RPC. (Inter-process communication/Remote-procedure calling)
  2. Project Purple. (iOS codename)
  3. GraphicsServices. (Private Framework)
  4. SpringBoard. (iOS GUI app)
  5. Runloop itself. (It's role in all of this)

I'm not really familiar with mach, (I'm planning to learn it more deeply in future) but I know basic primitives on which mach kernel operates. Mach kernel uses ports for communication between processes by sending messages. You can think of a process as your application.


Project purple is simple codename for iOS. Why I mention this? Because there is a mach 'Purple system event' port which is used to send system event for SpringBoard application.


As I know there are private GraphicsServices framework which has:

  • Checking for device capabilities. (Camera, Bluetooth, GPS, etc.)
  • Get screen size/dimensions, orientation
  • Presenting/Hiding/Managing keyboard
  • And of course sending events (touches, volume setting, silent ringer switch, device locking, etc.)

GraphicServices framework is sending all events using mach 'Purple system event' port.

Here you can look at its headers if you are interested:

https://github.com/rpetrich/iphoneheaders/tree/master/GraphicsServices


iOS have application called SpringBoard that is what user see when navigating on his iPhone/iPad/iPod and it's responsible for launching applications, sending them events receiving notifications from them and so on. And I think that you've already guessed that SpringBoard receives events from Purple system event port and almost all of them are passed to active application. (There are some events that only related for SpringBoard, for example device locking.)

For more information about SpringBoard look there:

http://theiphonewiki.com/wiki//System/Library/CoreServices/SpringBoard.app


And now what's the role of runloop in all of that? If you look at the source code for CFRunloopRef you can see that it works tightly with mach ports. (Source code can be found at : https://www.opensource.apple.com/source/CF/CF-476.10/CFRunLoop.c)

When you call CFRunLoopRun it simply waits for messages from port(s).

Application waits for messages from mach kernel

On the given screenshot you can see that UIApplicationMain function calls GraphicServices framework infinity loop for running modally and waiting for events via call mach_msg that enters in trap : mach_msg_trap. It puts thread into sleep and wake up when new event arrives. Also UIKit as I see registers its own callback for events that SpringBoard deliver. We will see later in stack trace PurpleEventCallback and _PurpleEventCallback function calls on event arriving. This functions acts as a bridge between all GraphicServices and UIKit stuff. (I mean we receive GSEvent that is wrapped by UIKit in UIEvent, etc.) You can see that function names are easter eggs for 'Project Purple' codename.

As for UIKit I assume that it register its own CFRunloopSourceRef that invokes _UIApplicationHandleEventQueue function and handles such stuff as touches and other. (delegates them to your application) See screenshot (How is touch handled by system):

Touching screen stack trace

And when application suspends - UIKit invalidates this sources. Look at screenshot. (I've set symbolic breakpoint on CFRunLoopRemoveSource and as you can see in stack trace UIKit prepares to suspend application.) Then I choose CFRunLoopSourceInvalidate frame to find out if this run loop source is related to mach stuff. The CFRunLoopSourceInvalidate method has following prototype :

void CFRunLoopSourceInvalidate ( CFRunLoopSourceRef source );

So using lldb and having knowledge that arguments should be stored in a registers on method invocation I've printed out register values and read suggested argument to be related to mach stuff. As you can see - it's true.

Invalidating runloop source


UIKit also notifies about successful launch, successful suspending etc. Whom it notifies? SpringBoard. There is specific method that used to send events for specific mach port called GSSendEvent(). Port passed there is the application's event port. For more information look:

http://iphonedevwiki.net/index.php/GSEvent

Also some screenshots that demonstrate this communication:

Application finished its launching and reports this back to SpringBoard:

Application finished launching

Application received event and you can see it's doing some internal things, setting status bar, instantiating view controller from storyboard, reporting back, etc:

Some events handled by UIKit

Suspending application call stack:

Suspending application

Summary:

GraphicServices uses 'Purple system event' port to send events related to touches, device locks, suspending active application etc. SpringBoard receives messages from 'Purple system event' port and sends them to active application by getting its event port. UIKit receives them, handles them, and may sends result back to SpringBoard using SpringBoard event port.


OTHER TIPS

Not necessarily. performSelector:withObject:afterDelay: and NSTimer will run on the runloop of the thread that called events them, whereas sendEvent: should run on the main runloop.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top