RunLoop这个东西,其实我们一直在用,但一直没有很好地理解它,或者甚至没有知道它的存在。RunLoop可以说是每个线程都有的一个对象,是用来接受事件和分配任务的loop。永远不要手动创建一个runloop,它是跟随着每个线程的。一个RunLoop接收两种source的事件:input source和timer source。同时必须知道的是,input source,runloop是异步交付的,而timer source是同步交付的。每个runloop都有一个RunLoop Modes,代表它以何种方式执行。

  我们为什么从来没有感觉到runloop的存在呢,是因为当程序启动,系统默认帮我们启动了一个主线程的runloop,并且一直在运行,直到程序退出。而用户创建的子线程,runloop是需要手动启动的,所以在线程里启动timer或者调用performSelector: withObject:afterDelay:inModes: 是需要启动runloop的。在后面我会介绍到怎么启动。

关于input source :一般来说,input source基本我们用的就是下面几种方式调用的:

Methods

Description

performSelectorOnMainThread: withObject:
waitUntilDone:
performSelectorOnMainThread: withObject:
waitUntilDone:modes:

Performs the specified selector on the application’s main thread during that thread’s next run loop cycle. These methods give you the option of blocking the current thread until the selector is performed.

performSelector: onThread:withObject:
waitUntilDone:
performSelector: onThread:withObject:
waitUntilDone:modes:

Performs the specified selector on any thread for which you have an NSThread object. These methods give you the option of blocking the current thread until the selector is performed.

   
performSelector: withObject: afterDelay:
performSelector: withObject:
afterDelay:inModes:

Performs the specified selector on the current thread during the next run loop cycle and after an optional delay period. Because it waits until the next run loop cycle to perform the selector, these methods provide an automatic mini delay from the currently executing code. Multiple queued selectors are performed one after another in the order they were queued.

cancelPreviousPerformRequestsWithTarget:
cancelPreviousPerformRequestsWithTarget:
selector:object:

Lets you cancel a message sent to the current thread using the performSelector: withObject: afterDelay: or performSelector: withObject: afterDelay:inModes: method.


这是系统帮我们封装了一层,非常容易调用。用户可以创建自己的Port-based input sources,用来监听某个端口的事件跟其他线程通信:
- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode;

- (void)removePort:(NSPort *)aPort forMode:(NSString *)mode;

就是利用NSPort对象进行的消息传递和delegate,我们一般用不了,有兴趣可以看看NSPort的介绍。

关于timer source:就是我们平时用到的NSTimer了。

至于我们什么时候需要用到runloop呢,主要是下面几种情况:

1.需要用到NSPort或者其他input source跟其他线程通信。

2.在线程启动timer。

3.在线程里调用performSelector...这类函数去调用。

下面我简单用一个例子怎么在线程里启动timer或者performSelector...如下:

-(void)testMain

{

  //开启一个测试子线程

  [NSThread detachNewThreadSelector:@selector(threadMethod) toTarget:self withObject:nil];

}

-(void)threadMethod

{

    //没用的timer

    //NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:.2 target:self selector:@selector(timerDone) userInfo:nil repeats:YES];

    //真正启动了timer

    NSTimer *timer = [NSTimerscheduledTimerWithTimeInterval:.2target:selfselector:@selector(timerDone) userInfo:nilrepeats:YES];

    [[NSRunLoopcurrentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

    [[NSRunLoopcurrentRunLoop] run];

   //同理,调用performSelector也一样
//[self performSelector:@selector(timerDone) withObject:nil afterDelay:.2];
//[[NSRunLoop currentRunLoop] run]; } -(void)timerDone { NSLog(@"Timer Run"); }

NStimer,几乎每个做iOS开发的程序员都用过,但是有一个关于Timer的介绍估计很多人都不知道:timer是不一定准时的,是有可能被delay的,每次间隔的时间是不一定一样的。

A repeating timer reschedules itself automatically based on the scheduled firing time, not the actual firing time. For example, if a timer is scheduled to fire at a particular time and every 5 seconds after that, the scheduled firing time will always fall on the original 5 second time intervals, even if the actual firing time gets delayed. If the firing time is delayed so much that it misses one or more of the scheduled firing times, the timer is fired only once for the missed time period. After firing for the missed period, the timer is rescheduled for the next scheduled firing time.

简单解读一下:就是说一个repeat的timer,它在创建的时候就把每次的执行时间算好了,而不是真正启动的时候才计算下次的执行时间。举个例子,假如一个timer在一个特定的时间t激活,然后以间隔5秒的时间重复执行。那么它的执行操作的时间就应该为t, t+5, t+10,... 假如它被延迟了,例如实际上timer在t+2的时候才启动,那么它下次执行的时间还是t+5,而并不是t+2+5,如果很不幸地在t+5还没有启动,那么它理应该在t执行的操作就跟下一次t+5的操作合为一个了。至于为什么会延迟呢,这就跟当前线程的操作有关,因为timer是同步交付的,所以假如当前线程在执行很复杂的运算时,那必须等待运算的完成才能调用timer,这就导致了timer的延迟。

我们就用一个例子来看看效果吧,代码为:

 //这里创建timer以每隔1秒执行
[NSTimer scheduledTimerWithTimeInterval: target:self selector:@selector(timerDone) userInfo:nil repeats:YES];
//这里在第3秒的时候模拟一个复杂运算
[self performSelector:@selector(busyDone) withObject:nil afterDelay:]; -(void)busyDone
{
//这里模拟线程复杂的运算
for(NSInteger i = ; i< 0xffffffff;i++){ }
NSLog(@"BusgDone");
} -(void)timerDone
{
NSLog(@"Timer Run");
}

执行结果为:

可以看到,timer本来都是以每隔1秒执行,毫秒都是.564,然后在进行复杂的运算时候,timer直接被delay了,当执行完BusyDone之后,立即执行了TimerRun,然后又在.564执行了TimerRun,而不是距离上次执行时间的1秒。

仅供参考。

iOS关于RunLoop和Timer的更多相关文章

  1. iOS多线程-RunLoop简介

    什么是RunLoop? 从字面上来看是运行循环的意思. 内部就是一个do{}while循环,在这个循环里内部不断的处理各种任务(比如:source/timer/Observer) RunLoop的存在 ...

  2. ios之runloop笔记

    网上关于runloop的文章不计其数,再此,贴个自认为讲的比较简单明了的文章 http://www.jianshu.com/p/536184bfd163 个人理解: ios的runloop应该是类似于 ...

  3. iOS开发RunLoop学习:四:RunLoop的应用和RunLoop的面试题

    一:RunLoop的应用 #import "ViewController.h" @interface ViewController () /** 注释 */ @property ( ...

  4. iOS - OC RunLoop 运行循环/消息循环

    1.RunLoop 1)运行循环: 运行循环在 iOS 开发中几乎不用,但是概念的理解却非常重要. 同一个方法中的代码一般都在同一个运行循环中执行,运行循环监听 UI 界面的修改事件,待本次运行循环结 ...

  5. iOS开发RunLoop

    最近处于离职状态,时间也多了起来,但是学习还是不能放松,今天总结一下RunLoop,RunLoop属于iOS系统层的东西,还是比较重要的. 一.什么是RunLoop 字面意思看是跑圈,也可以看作运行循 ...

  6. iOS之RunLoop

    RunLoop是iOS线程相关的比较重要的一个概念,无论是主线程还是子线程,都对应一个RunLoop,如果没有RunLoop,线程会马上被系统回收. 本文主要CFRunLoop的源码解析,并简单阐述一 ...

  7. iOS开发-Runloop详解(简书)

    不知道大家有没有想过这个问题,一个应用开始运行以后放在那里,如果不对它进行任何操作,这个应用就像静止了一样,不会自发的有任何动作发生,但是如果我们点击界面上的一个按钮,这个时候就会有对应的按钮响应事件 ...

  8. iOS开发RunLoop学习:一:RunLoop简单介绍

    一:RunLoop的简单介绍 #import "ViewController.h" @interface ViewController () @end @implementatio ...

  9. iOS开发 - RunLoop理解

    RunLoop概念 运行循环,一个 run loop 就是一个事件处理的循环,用来不停的调度工作以及处理事件 作用 保持程序的持续运行 监听处理App中的各种事件(触摸事件,定时器事件,selecto ...

随机推荐

  1. Android两个注意事项.深入了解Intent和IntentFilter(两)

    深入理解Intent和IntentFiler(二) 转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空)     在上一篇文章中,我们比較具体学习了&q ...

  2. crawler_如何从页面获取新浪cookie

    步奏如下: 1 用chrome浏览器(其他浏览器原理相同)打开地址: http://weibo.com/ 2.点击鼠标右键 右键点击查看元素 点击Network   3.输入用户名  密码 执行登录 ...

  3. java_linux_shell_定时kill 启动java程序

    #!/bin/bash #while truedo Process_ID=`ps -ef |grep 'LoginSinaWeiboCookie.jar' |grep -v grep |awk '{p ...

  4. Socket 学习(三).2 udp 穿透 服务端 与 客户端 通讯

    之前演示的 是 局域网通讯,也可以用作服务器之间的通讯,不能穿透. 想要穿透就要用 udp 了, 后续再讲解 udp 打洞 . 客户端: using System; using System.Wind ...

  5. MVC应用程序使用Wcf Service

    原文:MVC应用程序使用Wcf Service 前一篇Insus.NET有演示过MVC应用程序使用Web Service, 此篇Insus.NET想继续演示Service,不过是WCF Service ...

  6. protobuf-net-data

    protobuf-net http://www.codeproject.com/Articles/642677/Protobuf-net-the-unofficial-manual https://g ...

  7. thinkphp 支付宝错误 Class 'Think' not found

    Class 'Think' not found D:\www\DonatePlatform\ThinkPHP\Extend\Vendor\alipay\lib\alipay_submit.class. ...

  8. ios背景更新和下载

    ios背景更新和下载 by 吴雪莹 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NS ...

  9. Spring IOC 之Bean作用域

    当你创建一个bean定义的时候,你创建了一份通过那种bean定义的bean的创建类的真正实力的处方.bean的定义是一个处方 的想法是很重要的的.因为这意味着,对于一个类你可以创建很多对象实例从一个单 ...

  10. JS中的模块规范(CommonJS,AMD,CMD)

    JS中的模块规范(CommonJS,AMD,CMD) 如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已. 现在就看看吧, ...