zoukankan      html  css  js  c++  java
  • ios RunLoop解析

    本文转载自:http://www.dreamingwish.com/dream-2012/ios-multithread-program-runloop-the.html,仅供自己学习,不用做任何商业用途。尊重原创!这篇文章翻译的很好很详细,本文只转载了基本介绍。至于详细的数据源、观察者等都是怎么实现的,请参看原文。

    Run loops是线程相关的的基础框架的一部分。一个run loop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。使用run loop的目的是让你的线程在有工作的时候忙于工作,而没工作的时候处于休眠状态。

    Run loop的管理并不完全自动的。你仍然需要设计你的线程代码在合适的时候启动run loop并正确响应输入事件。Cocoa和Core Fundation都提供了run loop objects来帮助配置和管理你线程的run loop。你的应用程序不需要显式的创建这些对象(run loop objects);每个线程,包括程序的主线程都有与之对应的run loop object。只有辅助线程才需要显式的运行它的run loop。在Carbon和Cocoa程序中,主线程会自动创建并运行它run loop,作为一般应用程序启动过程的一部分。

    一、介绍

    run loop,是线程进入和被线程用来响应事件以及调用事件处理函数的地方。需要在代码中使用控制语句实现run
    loop的循环,也就是说,需要代码提供while 或者 for循环来驱动run loop。在这个循环中,使用一个runloop对象[NSRunloop currentRunloop]执行接收消息,调用对应的处理函数。

    Run loop接收输入事件来自两种不同的来源:输入源(input source)定时源timer source)。输入源传递异步事件,通常消息来自于其他线程或程序。定时源则传递同步事件,发生在特定时间或者重复的时间间隔。两种源都使用程序的某一特定的处理例程来处理到达的事件。

    输入源传递异步消息给相应的处理例程,并调用runUntilDate:方法来退出(在线程里面相关的NSRunLoop对象调用)。定时源则直接传递消息给处理例程,但并不会退出run loop。

    除了处理输入源,run loops也会生成关于run loop行为的通知(notifications)。注册的run loop观察者(run-loop Observers)可以收到这些通知,并在线程上面使用它们来做额外的处理。你可以使用Core Foundation在你的线程注册run-loop观察者。

    二、RunLoop Mode模式

    一个runloop mode就是input sources、timer和observers的集合。每次执行runloop,都需要指定一个mode。在期间,只有与该mode关联的source才会被监管和传递他们的事件,同样只有相关的observer被通知。其他mode关联的源,只有再它的那种模式下,才会运行,否则处于暂停状态。

       在代码中,mode的命名用string表示,有一些default mode和其他常用的modes。
    可以使用字符串来标识一个自定义的mode。新建的自定义mode,至少需要一个inputsources或者timers或者 observers。

      mode用来过滤掉你不想监听的sources,使得你想要的事件通过你代码中的循环。大数情况运行在系统的default
    mode中。对于辅助线程,可以使用自定义mode来防止低优先级的sources传递事件,这样如果当前操作是time-critical,可以省下资源。

      mode区分是基于事件的源,而不是基于事件的种类,例如,你不可以使用模式只选择处理鼠标按下或者键盘事件。

      系统的mode有:Default、Connection、Modal、Event tracking、Common modes。

    三、输入源

         传递异步事件,事件来源取决于输入源的种类:基于端口的输入源自定义输入源。基于端口的输入源监听程序相应的端口。自定义输入源则监听自定义的事件源。

        run loop并不关心输入源的是哪种种类。系统会实现两种输入源供你使用。两类输入源的区别在于如何显示:基于端口的输入源由内核自动发送,而自定义的则需要人工从其他线程发送。当你创建输入源,你需要将其分配给run loop中的一个或多个模式。

       1、基于端口的输入源

       系统支持使用端口相关的对象和函数来创建的基于端口的源。

      在Cocoa里,你不需要直接创建输入源。你只要简单的创建端口对象,并使用NSPort的方法把该端口添加到run
    loop。端口对象会自己处理创建和配置输入源。

       在Core Foundation,你必须人工创建端口和它的run loop源。

       在两种情况下,你都可以使用端口相关的函数(CFMachPortRef,CFMessagePortRef,CFSocketRef)来创建合适的对象。

    2、自定义输入源

        必须使用Core Foundation里面的CFRunLoopSourceRef类型相关的函数来创建。你可以使用回调函数来配置自定义输入源。Core Fundation会在配置源的不同地方调用回调函数,处理输入事件,在源从run loop移除的时候清理它。

        除了定义在事件到达时自定义输入源的行为,你也必须定义消息传递机制。源的这部分运行在单独的线程里面,并负责在数据等待处理的时候传递数据给源并通知它处理数据。消息传递机制的定义取决于你,但最好不要过于复杂。参阅CFRunLoopSource Reference。

    3、Cocoa 执行 Selector 的源<为自定义输入源准备的>

        Cocoa定义了自定义输入源,允许你在任何线程执行selector。和基于端口的源一样,执行selector请求会在目标线程上序列化,减缓许多在线程上允许多个方法容易引起的同步问题。不像基于端口的源,一个selector执行完后会自动从run loop里面移除。

    系统提供的方法:

    performSelectorOnMainThread:withObject:waitUntilDone:

    performSelectorOnMainThread:withObject:waitUntilDone:modes:

    performSelector:onThread:withObject:waitUntilDone:

    performSelector:onThread:withObject:waitUntilDone:modes:

    performSelector:withObject:afterDelay:

    performSelector:withObject:afterDelay:inModes:

    cancelPreviousPerformRequestsWithTarget:

    cancelPreviousPerformRequestsWithTarget:selector:object:这两个和上面两个配套使用,用来取消发送消息。

    四、定时源

        定时源在预设的时间点同步方式传递消息。重复工作定时器会基于安排好的时间而非实际时间调度它自己运行。举个例子,如果定时器被设定在某一特定时间开始并5秒重复一次,那么定时器会在那个特定时间后5秒启动,即使在那个特定的触发时间延迟了。如果定时器被延迟以至于它错过了一个或多个触发时间,那么定时器会在下一个最近的触发事件启动,而后面会按照触发间隔正常执行。查看NSTimer Class Reference或CFRunLoopTimer Reference。

    五、RunLoop观察者

         为了创建一个run loop观察者,你可以创建一个CFRunLoopObserverRef类型的实例。它会追踪你自定义的回调函数以及其它你感兴趣的活动。

         和定时器类似,run loop观察者可以只用一次或循环使用。若只用一次,那么在它启动后,会把它自己从run loop里面移除,而循环的观察者则不会。你在创建run loop观察者的时候需要指定它是运行一次还是多次。

    你可以将run loop观察者和以下事件关联:

    • Run loop入口
    • Run loop何时处理一个定时器
    • Run loop何时处理一个输入源
    • Run loop何时进入睡眠状态
    • Run loop何时被唤醒,但在唤醒之前要处理的事件
    • Run loop终止

    六、RunLoop的事件队列

    每次运行run loop,你线程的run loop对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:

    1. 通知观察者run loop已经启动
    2. 通知观察者任何即将要开始的定时器
    3. 通知观察者任何即将启动的非基于端口的源
    4. 启动任何准备好的非基于端口的源
    5. 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
    6. 通知观察者线程进入休眠
    7. 将线程置于休眠直到任一下面的事件发生:
      • 某一事件到达基于端口的源
      • 定时器启动
      • Run loop设置的时间已经超时
      • run loop被显式唤醒
    8. 通知观察者线程将被唤醒。
    9. 处理未处理的事件
      • 如果用户定义的定时器启动,处理定时器事件并重启run loop。进入步骤2
      • 如果输入源启动,传递相应的消息
      • 如果run loop被显式唤醒而且时间还没超时,重启run loop。进入步骤2
    10. 通知观察者run loop结束。
  • 相关阅读:
    覆盖一个DIV,让radio、checkbox和select只读
    DNN 配置 数据库篇
    Javascript实用语句收录
    绑定Hashtable到DataList
    WebDeploymentSetup 合并程序集时出错(ILMerge.Merge: ERROR)
    只能向页面中添加 ScriptManager 的一个实例
    让DNN添加的别名起作用
    360极速模式(Chrome内核)下由ashx输出的JavaScript代码不起作用
    C#中常用到的时间函数(天数差、星期几等)
    C#实用语句
  • 原文地址:https://www.cnblogs.com/wyqfighting/p/3254037.html
Copyright © 2011-2022 走看看