[UIApplication sendEvent:]是否在NSRunLoop中执行?

时间:2014-03-01 16:06:04

标签: ios objective-c cocoa-touch nsrunloop

我读了这个apple doc about runloop

  

运行循环是一个事件处理循环,用于调度工作并协调传入事件的接收...运行循环从两种不同类型的源接收事件。输入源提供异步事件...定时器源提供同步事件......

现在我知道performSelector:withObject:afterDelay:NSTimer在runloop中运行。

doc没有提到触摸事件作为输入源。我想知道:

Q1:[UIApplication sendEvent:]发送的触摸事件是否也会在某个默认的runloop中运行? Q2:如果Q1的答案为YES,那么默认的runloop是同一个runloop处理performSelector:withObject:afterDelay:NSTimer个事件吗?

2 个答案:

答案 0 :(得分:10)

我不会假装答案准确无误只是我的猜测。

我将在这个答案中介绍:

  1. Mach IPC / RPC。 (进程间通信/远程过程调用)
  2. Project Purple。 (iOS代号)
  3. GraphicsServices。 (私人框架)
  4. 跳板。 (iOS GUI应用程序)
  5. Runloop本身。 (它在所有这一切中的作用)

  6. 我并不熟悉马赫,(我计划将来更深入地学习马赫)但我知道马赫内核运作的基本原语。 Mach内核使​​用端口通过发送消息进程之间进行通信。您可以将流程视为您的应用程序。


    Project purple 是iOS的简单代号。我为什么提这个?因为有一个马赫'紫色系统事件'用于发送 SpringBoard 应用程序的系统事件的端口。


    据我所知,有私人 GraphicsServices框架 ,其中包含:

    • 检查设备功能。 (相机,蓝牙,GPS等)
    • 获取屏幕尺寸/尺寸,方向
    • 演示/隐藏/管理键盘
    • 当然发送活动触摸,音量设置,静音铃声开关,设备锁定等)

    GraphicServices框架使用mach'紫色系统事件'发送所有事件。端口。

    如果您有兴趣,可以在此处查看其标题:

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


    iOS有一个名为SpringBoard的应用程序,用户在iPhone / iPad / iPod上导航时会看到它,它负责启动应用程序,向它们发送接收来自它们的通知的事件等等。我认为你已经猜到SpringBoard从Purple系统事件端口接收事件,并且几乎所有事件都被传递给活动应用程序。 (有些事件只与SpringBoard有关,例如设备锁定。)

    有关SpringBoard的更多信息,请查看:

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


    现在, runloop 在所有这些方面的作用是什么?如果查看CFRunloopRef的源代码,可以看到它与mach端口紧密配合。 (源代码可在以下位置找到:https://www.opensource.apple.com/source/CF/CF-476.10/CFRunLoop.c

    当你拨打CFRunLoopRun时,它只是等待来自端口的消息。

    Application waits for messages from mach kernel

    在给定的屏幕截图中,您可以看到UIApplicationMain函数调用GraphicServices框架无限循环,以便通过调用mach_msg进入模块并等待事件进入陷阱:mach_msg_trap。它使线程进入睡眠状态,并在新事件到来时唤醒。另外 UIKit ,因为我看到为 SpringBoard 提供的事件注册了自己的回调。稍后我们将在事件到达的堆栈跟踪PurpleEventCallback_PurpleEventCallback函数调用中看到。这个函数充当所有GraphicServices和UIKit之间的桥梁。 (我的意思是我们在UIEvent中收到由UIKit包装的GSEvent等)你可以看到函数名是“紫色项目”的复活节彩蛋。代号。

    至于 UIKit 我假设它注册了自己的CFRunloopSourceRef,它调用_UIApplicationHandleEventQueue函数并处理触摸和其他内容。 (将它们委托给您的应用程序)查看屏幕截图(系统如何处理触摸):

    Touching screen stack trace

    当应用程序暂停时, UIKit 会使此来源无效。看截图。 (我在CFRunLoopRemoveSource上设置了符号断点,你可以在堆栈跟踪中看到UIKit准备挂起应用程序。)然后我选择CFRunLoopSourceInvalidate帧来查明这个运行循环源是否与马赫的东西。 CFRunLoopSourceInvalidate方法有以下原型:

    void CFRunLoopSourceInvalidate ( CFRunLoopSourceRef source );

    因此,使用lldb并且知道参数应该存储在方法调用的寄存器中,我打印出寄存器值并读取与mach内容相关的建议参数。如你所见 - 这是真的。

    Invalidating runloop source


    UIKit 也会通知成功发布,成功暂停等通知?的 跳板 即可。有一种特定的方法用于发送名为GSSendEvent()的特定机器端口的事件。通过那里的端口是应用程序的事件端口。有关更多信息,请查看:

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

    还有一些截图展示了这种沟通:

    应用程序完成了启动并将其报告回 SpringBoard

    Application finished launching

    应用程序收到事件,您可以看到它正在做一些内部事务,设置状态栏,从故事板实例化视图控制器,报告回来等等:

    Some events handled by UIKit

    暂停应用程序调用堆栈:

    Suspending application

    <强>要点:

    GraphicServices 使用&#39;紫色系统事件&#39;用于发送与触摸,设备锁定,挂起活动应用程序等相关的事件的端口 SpringBoard 接收来自&#39;紫色系统事件&#39;端口并通过获取其事件端口将它们发送到活动应用程序。 UIKit 接收它们,处理它们,并可能使用 SpringBoard将结果发送回 SpringBoard 事件端口。


答案 1 :(得分:1)

不一定。 performSelector:withObject:afterDelay:NSTimer将在调用事件的线程的runloop上运行,而sendEvent:应该在主runloop上运行。