【移动应用开发技术】IOS开发需要知道的知识-RunLoops_第1页
【移动应用开发技术】IOS开发需要知道的知识-RunLoops_第2页
【移动应用开发技术】IOS开发需要知道的知识-RunLoops_第3页
【移动应用开发技术】IOS开发需要知道的知识-RunLoops_第4页
【移动应用开发技术】IOS开发需要知道的知识-RunLoops_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】IOS开发需要知道的知识-RunLoops

什么是RunLoopsRunLoops是与线程想关联的基础部分。一个RunLoop就是事件处理循环,它是用来调度和协调接收到的事件处理。使用RunLoop的目的,就是使得线程有工作需要做时可以忙碌起来,而当没有事可做时,又可以使得线程睡眠。RunLoop管理不都是自动的。我们必须手动设计线程代码,在合适的时候来启动RunLoop,并回应到来的事件。Cocoa和CoreFoundation都提供了runloop对象来帮助我们配置和管理线程的runloop。我们的应用没有必要显式地创建这些对象;每个线程,包括应用程序的主线程,都有一个与之关联的runloop。只有子线程才需要显式地运行其runloop。App会将自动配置和来运行主线程的runloop的任务作为应用程序启动处理的一部分。对于想要更深入地了解RunLoopObjects,阅读NSRunLoopClassReference、CFRunLoopReference一个RunLoop的结构RunLoop就像它的名字一样,它使得线程进入事件循环,能对到来的事件启动事件处理。你的代码中提供了流程控制说一句来实现runloop实实在在的循环部分,换句话说,你的代码提供了while或者for循环来驱动runloop。在你的循环中,你使用runloop对象在事件到达时,运行事件处理的代码并调起已安装的处理程序。RunLoop接收来自两种不同类型的源(sources)的事件:输入源:异步传递事件,通常是来自不同的线程或不同的应用的消息。输入源异步传递事件到对应的处理程序和在线程关联的NSRunLoop对象调起runUntilDate:方法来退出事件处理。Timer源:同步地传递事件,发生在每个定时器调用或周期性地调用。Timer源传递事件到他们的处理程序,但是不会调用runloop来退出处理。这两种源在事件到达时都使用应用程序特定的处理程序来处理事件。如下图所示,展示了runloop和不同的源的概述结构。p_w_picpath除了处理输入源之外,runloops还发出关于runloop行为的通知。我们可以注册成为runloop的观察者,就可以接收这些通知和使用它在线程上做一些额外处理。我们可以使用CoreFoundation在对应的线程上注册成为runloop的观察者。RunLoopModesRunLoop模式是一个监视输入源和定时器的集合和注册成为runloop的观察者的集合。每次要运行runloop,都需要显示或隐式地指定某种运行的mode。只有与这种指定的mode关联的源才会被监视和允许传递他们的事件,同样地,只有与这种模式关联的观察者都会收到runloop行为变化的通知。与其它模式想关联的源,直到随后在合适的模式通过循环后,都会接收到新的事件(比如,将timer加入runloopdefault模式下,当滚动时,timer不会收到回调,直到停止滚动回到default模式下)。在我们的代码中,我们通过名称来唯一标识mode。在Cocoa和CoreFoundation中都定义了default模式和几个常用的模式,都是通过字符串名称来指定。我们也可以自定义模式,但是我们需要手动添加至少一个inputsource/timers/observers。我们可以通过使用mode来过滤掉我们不希望接收到来自不想要的通过runloop的源。大部分情况下,我们都是使用系统定义的default模式。对于子线程,我们可以使用自定义模式在关键性操作时阻止低优先级的源传递事件。注意:Modes是通过事件源来区分,而不是事件类型来区分。比如说,我们不能使用mode来匹配只有mouse-down事件或者只有键盘事件。我们可以使用modes来监听不同系统的端口,临时挂起定时器,甚至改变正在被监视的sources和runloop观察者。表格.pngInputSources输入源异步传递事件到你的线程。事件的源由输入源的类型来决定,也就是两种源中的其中一种:Port-based:基于端口号的输入源监听应用程序的Mach端口。CustomInputSources:自定义输入源监听自定义的事件源。系统通常实现了这两种输入源。唯一的不同点是它们是如何被发出信号的。port-based源是由内核(kernel)自动发出信号,而customsources必须手动从其它线程发出信号。当我们创建输入源时,可以指定mode。Modes会影响任何时刻被监视的输入源。大部分情况下,我们都让runloop在defaultmode下运行,但是也可以指定自定义的mode。如果一个输入源不是当前所监视的model,它所产生的任何事件都会被保留直接进入正常的mode。Port-BasedSourcesCocoa和CoreFoundation提供了内建支持,可以使用与port相关的对象和函数来创建基于端口的输入源。举个例子,在Cocoa中永远不需要手动创建输入源。我们只需要简单地创建一个port对象和使用NSPort的方法。port对象为我们处理所需要的输入源的创建和配置。在CoreFoundation中,我们必须手动创建port和source。在这两种情况下,我们可以使用与portopaquetype关联的函数(CFMessagePortRef,orCFSocketRef)来创建合适的对象。CustomInputSources在CoreFoundation中,要创建自定义输入源,我们必须使用与CFRunLoopSourceRef关联的函数。我们配置自定义输入源可以使用几个回调函数。CoreFoundation会在不同点回调这些函数来配置source,处理任何到达的事件和销毁已从runloop移除的source。除了定义在事件到达时自定义源的行为之外,我们也必须定义事件传递机制。这部分源运行在单独的线程,负责提供输入源的数据,当数据准备好可以处理时,signaling(通知相关线程)这个消息。事件传递机制是我们自己来决定,但是不需要过于复杂。CocoaPerformSelectorSource除了基于端口的源之外,Cocoa还定义了自定义输入源允许我们在任意线程上执行selector。就像port-based源一样,执行selector请求会在目标线程上序列化,以减少在同一个线程中出现多个方法同步执行的问题。与port-based源不同的是,执行selector源在执行完毕后会自动将自己从runloop中移除。当执行在其它线程执行selector时,目标线程必须要有运行的runloop。当我们创建线程时,这意味着直到启动了runloop都会显式地执行selector代码。RunLoop每次经过一个循环,就会处理队列中所有的selector,而不仅仅是处理一个。方法和说明。TimerSourcesTimer源在未来设定的时间会同步地传递事件到你的线程。Timers是线程通知自己去做一些事情的一种方式。比如说,搜索框可以使用定时器来初始化在一定时间就自动搜索,以便提供更多地联想词给用户。尽管它发送基于时间的通知,但定时器并不是一种实时的机制。像输入源一样,定时器只有与runloop的mode一样才会发送通知。如果timer在runloop中并不是所被监视的mode,它不会触发定时器,直到runloop的mode与timer所支持的mode一样。同样地,如果runloop正在处理中,timer已经fire了,这时候会被中断,直到下一次通过runloop才会调志处理程序。如果runloop已经不再运行了,则timer永远不会再fire。我们可以配置timer只产生事件一次或者重复产生。重复的timer会自动根据调度的firingtime自动调度,而不是真实的firingtime。比如说,如果一个timer在特定的时间调度,然后每5秒重复一次。如果firingtime被延迟导致缺少一或多次调用,那么timer在缺失的周期中只会调用一次。RunLoopObservers与sources在适当时机异步或同步发出事件不同,observers在runloop本身执行期间,会在特定的地方发出。你可能需要到runloopobservers去准备线程处理特定的事件或者在进入睡眠之前。我们可以通过以下事件来关联runloopobservers:进入run

looprun

loop将要处理timer

run

loop将要处理输入源

run

loop将要进入睡眠

run

loop被唤醒,但是还没有处理事件

退出run

loop我们可以通过CoreFoundation来添加runloopobservers。要创建runloopobserver,可以通过CFRunLoopObserverRef来创建新的实例。这个类型会跟踪你所定义的回调函数和所感兴趣的活动。与timers类型,run-loopobservers可以使用一次或者重复多次。一次性的observer会在fire之后自动从runloop移除,而重复性的observer会继续持有。TheRunLoopSequenceOfEvents本小节讲的是RunLoop事件顺序。每次运行它,你的线程的runloop处理待处理的事件和给所有attachedobservers发出通知。处理的顺序如下:1.通知observers

run

loop已经进入2.通知observers

timers准备要fire3.通知observers有不是基于port-based的输入源即将要fire4.fire任何已经准备好的non-port-based输入源5.如果port-based输入源准备好且等待fire,则立即处理这个事件。然后进入步骤96.通知observers线程即将进入睡眠7.让线程进入睡眠,直到以下任何一种事件到达:

*

port-based输入源有事件到达

*

timer

fire

*

run

loop超时

*

run

loop被显式唤醒8.通知observers线程被唤醒9.处理待处理的事件:

*

如果用户定义的timer

fired了,处理timer事件并重新启动循环。进入步骤2

*

如果输入源fired了,则传递事件

*

如果run

loop被显式唤醒,但是又未超时,则重启循环,进入步骤210.通知observers

run

loop退出由于observer对timer和输入源的通知会在事件真正发生之前被传递,这样就产生了间隙。如果这个间隙是很关键的,那么我们可以通过使用sleep和awake-from-sleep通知来帮助我们纠正这个时间间隔问题。WhenWouldYouUseARunLoop?什么时候应该使用runloop呢?只有当我们需要创建子线程的时候,才会需要到显示地运行runloop。应用程序的主线程的runloop是应用启动的基础任务,在启动时就会自动启动runloop。所以我们不需要手动启动主线程的runloop。对于子线程,我们需要确定线程是否需要runloop,如果需要,则配置它并启动它。我们并不问题需要启动runloop的。比如说,如果我们开一个子线程去执行一些长时间的和预先决定的任务,我们可能不需要启动runloop。Runloop是用于那么需要在线程中有更多地交互的场景。比如说,我们会在下面的任何一种场景中需要开启runloop:使用端口源或者自定义输入源与其它线程通信

在线程中使用定时器

使用Cocoa中的任何performSelector…方法

保持线程来执行周期性的任务UsingRunLoopObjectsRunLoop对象给添加输入源、定时器和观察者到runloop提供了主接口。每个线程都有一个单独的runloop与之关联(对于子线程,若没有调用过任何获取runloop的方法是不会有runloop的,只有调用过,才会创建或者直接使用)。在Cocoa中,通过NSRunLoop来创建实例,在low-level应用中,可以使用CFRunLoopRef类型,它是指针。GettingARunLoopObject通过以下两种方式来获取runloop对象:在Cocoa中,使用[NSRunLoopcurrentRunLoop]获取使用CFRunLoopGetCurrent()函数获取配置RunLoop在子线程运行runloop之前,你必须至少添加一种输入源或者定时器。如果runloop没有任何的源需要监视,它就会立刻退出。除了添加sources之外,你还可以添加观察者来检测runloop不同的执行状态。要添加观察者,可以使用CFRunLoopObserverRef指针类型和使用CFRunLoopAddObserver函数来添加到runloop中。我们只能通过CoreFoundation来创建runloop观察者,即使是Cocoa应用。下面这段代码展示主线程如何添加观察者到runloop以及如何创建runloop观察者:-

(void)threadMain

{

//

应用程序使用垃圾收集,因此不需要autorelease池。

NSRunLoop*

myRunLoop

=

[NSRunLoop

currentRunLoop];

//

创建一个运行循环观察者并将它附加到运行循环。

CFRunLoopObserverContext

context

=

{0,

self,

NULL,

NULL,

NULL};

CFRunLoopObserverRef

observer

=

CFRunLoopObserverCreate(kCFAllocatorDefault,

kCFRunLoopAllActivities,

YES,

0,

&myRunLoopObserver,

&context);

if

(observer)

{

CFRunLoopRef

cfLoop

=

[myRunLoop

getCFRunLoop];

CFRunLoopAddObserver(cfLoop,

observer,

kCFRunLoopDefaultMode);

}

//创建和安排计时器。

[NSTimer

scheduledTimerWithTimeInterval:0.1

target:self

selector:@selector(doFireTimer:)

userInfo:nil

repeats:YES];

NSInteger

loopCount

=

10;

do

{

//

运行运行循环10次让计时器。

[myRunLoop

runUntilDate:[NSDate

dateWithTimeIntervalSinceNow:1]];

loopCount--;

}

while

(loopCount);

}在为长时间存在的线程配置run-loop时,最好添加至少一个输入源来接收事件。尽管我们可以只用timer源,但是一旦timer调用后,经常会被invalidate,这会导致runloop退出。StartingtheRunLoop只有子线程才有可能需要启动runloop。Runloop必须至少有一种输入源或者timer源来监视。如果没有任何源,则runloop会退出。下面的几种方式可以启动runloop:无条件地:无条件进入runloop是最简单的方式,但也是最不希望这么做的,因为这样会导致runloop会进入永久地循环。可以添加、删除输入源和timer源,但是只能通过kill掉runloop才能停止。而且还不能使用自定义mode。限时:与无条件运行runloop不同,最好是给runloop添加一个超时时间。在特定的mode:除了添加超时时间,还可以指定mode。下面是运行runloop的一段代码:-

(void)skeletonThreadMain

{

//建立一个autorelease如果不使用垃圾收集池。

BOOL

done

=

NO;

//添加你的来源或计时器运行循环,做其他任何设置。

do

{/

/启动运行循环但每个源处理后返回。

SInt32

result

=

CFRunLoopRunInMode(kCFRunLoopDefaultMode,

10,

YES);

/

/如果源明确停止运行循环,或如果没有源或计时器,然后退出。

if

((result

==

kCFRunLoopRunStopped)

||

(result

==

kCFRunLoopRunFinished))

done

=

YES;

/

/检查任何其他退出条件和设置完成所需的变量。

}

while

(!done);

/

/清理代码。一定要释放任何生成自动分配池。

}ExitingtheRunLoop有两种方法使runloop在处理事件之前,退出runloop:给run

loop设定超时时间

告诉run

loop要stop设定超时时间是比较推荐的。我们可以通过CFRunLoopStop函数来停止runloop。ThreadSafetyandRunLoopObjectsCoreFoundation中的RunLoopAPI是线程安全的(以CF开头的API),而Cocoa中的NSRunLoop不是线程安全的。ConfiguringRunLoopSources下面是展示如何配置不同类型的输入源。DefiningaCustomInputSource创建自定义输入源涉及到以下部分:想要处理的输入源的信息让感兴趣的客户端知道如何联系输入源的调度程序执行任何客户端发送的请求处理程序使输入源失效的取消程序TimerSourceNSRunLoop*

myRunLoop

=

[NSRunLoop

currentRunLoop];

/

/创建和安排第一个定时器。NSDate*

fut

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论