NSRunloop顾名思义,就是一个消息循环,它会侦测输入源(input source)和定时源(timer source),而后作回调处理。这和windows的消息处理很是相似,只不过你没法看到相似SendMessage,PostMessage,GetMessage的方法,NSRunloop已经封装了这些细节。那NSRunloop的好处是否是只有封装细节,而后方便调用呢?结果是否认的。看apple官方文档(多线程编程指南)描述: "run loop 是用来在线程上管理事件异步到达的基础设施......run loop在没有任何事件处理的时候会把它的线程置于休眠状态,它消除了消耗CPU周期轮询,并防止处理器自己进入休眠状态并节省电源。" 看见没,消除CPU空转才是它最大的用处。php
因此NSRunloop的重点就是:html
1.run loop 用来监听长耗时的异步事件,若是用不到异步事件,就不用扯这个东西了(给你的面试官这么说吧)。例如,网络回调,不管是apple提供的NSURL仍是开源的ASIHttpRequest,都是用NSRunloop来监听网络事件(TCP/IP的堆栈)。(这我的的经历能够参考 http://www.blogjava.net/writegull/archive/2012/07/25/383926.html)java
2.run loop解决了CPU空转。面试
工做原理图以下编程
图 3-1显示了run loop的概念结构以及各类源。输入源传递异步消息给相应的处理例程,并调用runUntilDate:方法来退出(在线程里面相关的NSRunLoop对象调用)。定时源则直接传递消息给处理例程,但并不会退出run loop。 windows
从上图中咱们能够看出,每一个线程都有一个默认的NSRunloop。主线程的NSRunloop默认是运行的。非主线程的NSRunloop默认是没有运行的,须要为NSRunloop添加一个事件,而后去run,通常状况下没有必要启用线程的runloop,除非须要长久地监测某个异步事件。api
拿具体的应用举个例子,NSURLConnection网络数据请求,默认是异步的方式,其实现原理就是建立以后将其做为事件源加入到当前的 RunLoop,而等待网络响应以及网络数据接受的过程则在一个新建立的独立的线程中完成,当这个线程处理到某个阶段的时候好比获得对方的响应或者接受完了网络数据以后便通知以前的线程去执行其相关的delegate方法。因此在Cocoa中常常看到scheduleInRunLoop:forMode: 这样的方法,这个即是将其加入到事件源中,当检测到某个事件发生的时候,相关的delegate方法便被调用。网络
先提出一个问题,在Iphone项目中,你们会看到一个默认的Autorelease pool,程序开始时建立,程序退出时销毁,按照对Autorelease的理解,岂不是全部autorelease pool里的对象在程序退出时才release, 这样跟内存泄露有什么区别?结果是,对于每个Runloop, 系统会隐式建立一个Autorelease pool,这样全部的release pool会构成一个象CallStack同样的一个栈式结构,在每个Runloop结束时,当前栈顶的Autorelease pool会被销毁,这样这个pool里的每一个Object会被release。多线程
那什么是一个runloop?一个UI事件,一个timer,一个系统delegate都称之为runloop(不是NSRunloop),runloop其实是从接收消息,而后处理完消息的一个完整过程。app
为了更加形象说明auto release pool机制,下面举例:
NSString* str1是assign。
UI事件:UIButton的target-action机制,在action中建立一个autorelease的UILabel对象,并赋值,在action中打印出值,action执行完毕,这个时候runloop结束,autorelease pool被释放,label也被释放,因此再调用这个对象的值时,出现bad_exec_access。
多说一句,只有以上提到了3种runloop才会自动建立autorelease pool,thread是不会自动建立的,因此咱们能够看到子线程中会有手动写的autorelease pool代码。这点之前搞混过,切记!
就我的经验而言,在iphone线程中使用异步NSURLConnection的经验能够说是一个彻底和愉悦搭不上边的事情。他给我带来的麻烦可真很多。例如,前几天,帮客户定位一个问题的时候发生的事情。
事情通过是这样的:客户反馈,没法正常使用咱们提供的某个和网络相关的功能,网络回调没有收到。可是其余回调能够正常工做,而且全部回调都是以一样的逻辑放在某个地方的。
我先确认了他的使用方式是否正确,并确认了输入参数的正确性,而且验证了回调的正确设置以及回调函数的使用无误。一切都没有问题,可是就是没法收到回调。
一切都那么的神奇,功能的调用并无什么特殊的,可是就单单这个功能工做不正常。中间又通过一些确认,发现数据并无到达服务端。可是一样的使用方式在咱们本身的环境下工做又是正常的。
花了将近5个小时,仍是没有发现缘由,就在我准备放弃的时候,忽然想到,莫非他的调用位置是线程中调用?通过确认,发现果真是在线程中调用的!让他们使用performOnMainThread的方式调用,终于解决了问题。
之前遇到过一次在线程中调用异步网络的状况,请教同事,同事告知,异步网络须要本身的RunLoop,因此要在线程中使用异步网络必须有本身的RunLoop,能够将他放到主线程的RunLoop。
可是,这样子,多线程的性能必然被下降,由于这样网络的工做就都是在主线程中完成的。同时怀疑,这样子的网络性能设计不是很低下!
恰好有点时间,就仔细的翻了一下苹果的开发文档,发现其中指出,全部的NSThread都有一个属于本身的NSRunLoop,而NSURLConnection的回调都会回调到当前线程中!
这个和我目前遇到的彻底不同!我没法在当前线程中收到回调!
又仔细在网络上搜索以后发现,原来,是由于网络回调的时候线程的NSRunLoop已经被无效的缘由。
咱们常见的线程中网络调用是这样的:
- (void)threadFunc
{
NSAutoReleasePool *pool = [NSAutoReleasePool new];
//do something
……
//send your request
[NSURLConnection connectionWithRequest:xxx];
//do some other thing
……
[pool release];
}
通常来讲,这个函数会很快走完,线程就结束了。因此,当网络响应回来的时候,你的线程已经结束了。你的网络回调依赖于该线程的NSRunLoop,可是线程已经结束了,因此你的网络回调就没法收到!!同理,全部的performAfterDelay之类的api以及NSTimer的在线程中工做都不正常!
那么,该如何让他正常工做呢?很简单,作完事情以前,让线程不要结束,保持空转状态就好了。
- (void)threadFunc
{
NSAutoReleasePool *pool = [NSAutoReleasePool new];
//do something
……
//send your request
[NSURLConnection connectionWithRequest:xxx];
//do some other thing
……
while(shouldExit) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } [pool release]; }