文档章节

NSRunloop,runloop,autoReleasePool和thread的关系理解及案例解决

 北方人在上海
发布于 2016/05/23 11:56
字数 1920
阅读 215
收藏 0
点赞 2
评论 0

1.NSRunloop

NSRunloop顾名思义,就是一个消息循环,它会侦测输入源(input source)和定时源(timer source),然后做回调处理。这和windows的消息处理非常类似,只不过你无法看到类似SendMessage,PostMessage,GetMessage的方法,NSRunloop已经封装了这些细节。那NSRunloop的好处是不是只有封装细节,然后方便调用呢?结果是否定的。看apple官方文档(多线程编程指南)描述: "run loop 是用来在线程上管理事件异步到达的基础设施......run loop在没有任何事件处理的时候会把它的线程置于休眠状态,它消除了消耗CPU周期轮询,并防止处理器本身进入休眠状态并节省电源。" 看见没,消除CPU空转才是它最大的用处。

所以NSRunloop的重点就是:

1.run loop 用来监听长耗时的异步事件,如果用不到异步事件,就不用扯这个东西了(给你的面试官这么说吧)。例如,网络回调,无论是apple提供的NSURL还是开源的ASIHttpRequest,都是用NSRunloop来监听网络事件(TCP/IP的堆栈)。(这个人的经历可以参考 http://www.blogjava.net/writegull/archive/2012/07/25/383926.html)

2.run loop解决了CPU空转。

 

工作原理图如下

 

 

图 3-1显示了run loop的概念结构以及各种源。输入源传递异步消息给相应的处理例程,并调用runUntilDate:方法来退出(在线程里面相关的NSRunLoop对象调用)。定时源则直接传递消息给处理例程,但并不会退出run loop。 

从上图中我们可以看出,每个线程都有一个默认的NSRunloop。主线程的NSRunloop默认是运行的。非主线程的NSRunloop默认是没有运行的,需要为NSRunloop添加一个事件,然后去run,一般情况下没有必要启用线程的runloop,除非需要长久地监测某个异步事件。

 

拿具体的应用举个例子,NSURLConnection网络数据请求,默认是异步的方式,其实现原理就是创建之后将其作为事件源加入到当前的 RunLoop,而等待网络响应以及网络数据接受的过程则在一个新创建的独立的线程中完成,当这个线程处理到某个阶段的时候比如得到对方的响应或者接受完了网络数据之后便通知之前的线程去执行其相关的delegate方法。所以在Cocoa中经常看到scheduleInRunLoop:forMode: 这样的方法,这个便是将其加入到事件源中,当检测到某个事件发生的时候,相关的delegate方法便被调用。

 

 

2.runloop和 autorelease pool

先提出一个问题,在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实际上是从接收消息,然后处理完消息的一个完整过程。

 

为了更加形象说明auto release pool机制,下面举例:

NSString* str1是assign。

UI事件:UIButton的target-action机制,在action中创建一个autorelease的UILabel对象,并赋值,在action中打印出值,action执行完毕,这个时候runloop结束,autorelease pool被释放,label也被释放,所以再调用这个对象的值时,出现bad_exec_access。

 

3.autorelease pool和thread

多说一句,只有以上提到了3种runloop才会自动创建autorelease pool,thread是不会自动创建的,所以我们可以看到子线程中会有手动写的autorelease pool代码。这点以前搞混过,切记!

 

iphone线程中使用异步网络的悲催经历

 

就个人经验而言,在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];
}

© 著作权归作者所有

共有 人打赏支持
粉丝 22
博文 187
码字总数 265578
作品 0
浦东
【Runloop】深入理解原理和机制

用runloop解决cpu空转 runloop运行模式 了解设置模式 runloop在哪里 使用方法 换起runloop的三种方式 额外了解

创意总监 ⋅ 2016/06/01 ⋅ 0

iPhone开发之NSRunLoop的进一步理解

iPhone应用开发中关于NSRunLoop的概述是本文要介绍的内容,NSRunLoop是一种更加高明的消息处理模式,他就高明在对消息处理过程进行了更好的抽象和封装,这样才能是的你不用处理一些很琐碎很低...

andyhe91 ⋅ 2014/04/14 ⋅ 1

对,我就是RunLoop(代码也会讲故事系列)

"请问你是?" "不用请问,我就是RunLoop" “你好,我是iOS开发者,我听说过你,不过抱歉,对你的名声我早有耳闻,只是不很熟悉。” ”嗯,不难理解。毕竟我在幕后,你在台前,我是说句不妄言...

CoderDancer ⋅ 2017/06/12 ⋅ 0

iOS底层探究-Runloop

Runloop 1. 概述 一般来说,一个线程只能执行一个任务,执行完就会退出,如果我们需要一种机制,让线程能随时处理时间但并不退出,那么 RunLoop 就是这样的一个机制。Runloop是事件接收和分发...

极客学伟 ⋅ 05/16 ⋅ 0

NSRunLoop详解

1.NSRunLoop是IOS消息机制的处理模式 NSRunLoop的主要作用:控制NSRunLoop里面线程的执行和休眠,在有事情做的时候使当前NSRunLoop控制的线程工作,没有事情做让当前NSRunLoop的控制的线程休...

ericChen ⋅ 2015/03/16 ⋅ 0

各个线程 Autorelease 对象的内存管理

最近和 bestswifter 、kuailejim 搞了一套模拟面试,然后不管是应届生还是工作两三年的高级工程师都对下面这几个问题比较懵逼,可能是开发中用到的不多,在这里浅浅的讨论下 Autoreleasepoo...

Joy_xx ⋅ 2017/11/15 ⋅ 0

performSelector:相关的知识

来看一个例子: #import <Foundation/Foundation.h> @interface MyTst : NSObject - (void) print; @end @implementation MyTst - (void) print { NSLog(@"xxxxxxxxxx"); } @end #import <UI......

SunGiantor ⋅ 2015/05/22 ⋅ 0

10个迷惑新手的Cocoa,Objective-c开发难点和问题

首先请谅解我可能使用很多英文,毕竟英文资料将来会是你的主要资料来源。 在你继续深入学习之前,请停下脚步弄清这些问题。如果你是新手,这个教程不要希望一次能看的非常透彻,学一定阶段反...

junwong ⋅ 2012/03/01 ⋅ 1

iOS中RunLoop机制浅探

iOS中RunLoop机制浅探 一、浅识RunLoop RunLoop这个家伙在iOS开发中,我们一直在用,却从未注意过他,甚至都不从见过他的面孔,那个这个神秘的家伙究竟是做什么的?首先,我们先来观察一下我...

珲少 ⋅ 2015/08/13 ⋅ 3

知其然亦知其所以然-NSOperation并发编程

对于iOS的并发编程, 用的最普遍的就是GCD了, GCD结合Block可以so easy的实现多线程并发编程. 但如果你看一些诸如AFNetworking, SDWebImage的源码, 你会发现它们使用的都是NSOperation, 纳尼?...

金小俊 ⋅ 2015/11/28 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Cube、Cuboid 和 Cube Segment

1.Cube (或Data Cube),即数据立方体,是一种常用于数据分析与索引的技术;它可以对原始数据建立多维度索引。通过 Cube 对数据进行分析,可以大大加快数据的查询效率 2.Cuboid 在 Kylin 中特...

无精疯 ⋅ 34分钟前 ⋅ 0

github太慢

1:用浏览器访问 IPAddress.com or http://tool.chinaz.com 使用 IP Lookup 工具获得github.com和github.global.ssl.fastly.net域名的ip地址 2:/etc/hosts文件中添加如下格式(IP最好自己查一...

whoisliang ⋅ 36分钟前 ⋅ 0

非阻塞同步之 CAS

为解决线程安全问题,互斥同步相当于以时间换空间。多线程情况下,只有一个线程可以访问同步代码。这种同步也叫阻塞同步(Blocking Synchronization). 这种同步属于一种悲观并发策略。认为只...

长安一梦 ⋅ 47分钟前 ⋅ 0

云计算的选择悖论如何对待?

人们都希望在工作和生活中有所选择。但心理学家的调查研究表明,在多种选项中进行选择并不一定会使人们更快乐,甚至不会产生更好的决策。心理学家Barry Schwartz称之为“选择悖论”。云计算为...

linux-tao ⋅ 49分钟前 ⋅ 0

我的第一篇个人博客

虽然这是个技术博客,但是,我总是想写一些自己的东西,所有就大胆的在这里写下了第一篇非技术博客。技术博客也很久没有更新,个人原因。 以后自己打算在这里写一些非技术博客,可能个人观点...

Mrs_CoCo ⋅ 50分钟前 ⋅ 0

Redis 注册为 Windows 服务

Redis 注册为 Windows 服务 redis 注册为 windows 服务相关命令 注册服务 redis-server.exe –service-install redis.windows.conf 删除服务 redis-server –service-uninstall 启动服务 re......

Os_yxguang ⋅ 50分钟前 ⋅ 0

世界那么大,语言那么多,为什么选择Micropython,它的优势在哪?

最近国内MicroPython风靡程序界,是什么原因导致它这么火呢?是因为他功能强大,遵循Mit协议开源么? 错!因为使用它真的是太舒服了!!! Micropython的由来,这得益于Damien George这位伟大...

bodasisiter ⋅ 54分钟前 ⋅ 0

docker 清理总结

杀死所有正在运行的容器 docker kill $(docker ps -a -q) 删除所有已经停止的容器(docker rm没有加-f参数,运行中的容器不会删掉) docker rm $(docker ps -a -q) 删除所有未打 dangling 标...

vvx1024 ⋅ 今天 ⋅ 0

关于学习

以前学车的时候,教练说了这样的一句话:如果一个人坐在车上一直学,一直学,反而不如大家轮流着学。因为一个人一直学,就没有给自己留空间来反思和改进。而轮流着学的时候大家下来之后思考上...

mskk ⋅ 今天 ⋅ 0

压缩工具之gzip-bzip2-xz

win下常见压缩工具:rar zip 7z linux下常见压缩工具:zip gz bz2 xz tar.gz tar.bz2 tar.xz gzip 不支持目录压缩 gzip 1.txt #压缩。执行后1.txt消失,生成1.txt.gz压缩文件 gzip -d 1.txt....

ZHENG-JY ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部