iOS关于RunLoop和Timer

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:1 target:self selector:@selector(timerDone) userInfo:nil repeats:YES];
 //这里在第3秒的时候模拟一个复杂运算
 [self performSelector:@selector(busyDone) withObject:nil afterDelay:3];
    
-(void)busyDone
{
    //这里模拟线程复杂的运算
    for(NSInteger i = 0; i< 0xffffffff;i++){
        
    }
    NSLog(@"BusgDone");
}

-(void)timerDone
{
    NSLog(@"Timer Run");
}
复制代码

 

执行结果为:

 

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值