NStimer使用要点之必备知识点三

本文介绍了NSTimer在RunLoop中的使用,特别是在不同模式下的行为。通过代码示例展示了如何在UIViewController的生命周期中添加定时器,并讨论了NSRunLoopCommonModes模式的作用,使得定时器在默认模式和追踪模式下都能正常工作。同时,文章提到了在一个子线程中使用NSTimer的情况,解释了为何在这种情况下定时器不会按预期工作。
摘要由CSDN通过智能技术生成

接上一篇

**Case One:**
我们用timerWithTimeInterval方法实例化了一个定时器,并手动加入当前运行循环,采用NSDefaultRunLoopMode默认模式。可以看到一切正常,每隔一秒种打印一次“print a line ---- ”
- (void)viewDidLoad {
    [super viewDidLoad];
    //You must add the new timer to a run loop
    NSTimer *timer = [NSTimer **timerWithTimeInterval**:1.0 target:self selector:@selector(printSth) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:**NSDefaultRunLoopMode**];
}
-(void)printSth
{
    NSLog(@"print a line ---- ");
}

**Case Two:**
在页面上拉一个TextView控件,Lorem 数字+Tab 可以生成随意多的内容,运行app,正常打印,当滚动的时候,手指不离开屏幕,这时会发现,定时器不打印了!
这是因为:
//    Mode:runloop在同一时间只能处理一个模式下的事件,当用户滑动TextView,runloop处理用户交互模式时,就不会处理时钟事件模式了
//    NSRunLoopCommonModes:他不是一个真正的模式,是一个占位模式
//    UITrackingRunLoopMode:用户交互模式,只要有触摸,马上处理
//    NSDefaultRunLoopMode:默认模式,网络/timer事件放在这里

2017-08-29 17:09:18.973508+0800 NStimer[2560:665239] print a line ----
2017-08-29 17:09:19.974232+0800 NStimer[2560:665239] print a line ----
2017-08-29 17:09:20.974243+0800 NStimer[2560:665239] print a line ----

2017-08-29 17:09:28.251730+0800 NStimer[2560:665239] print a line ----
2017-08-29 17:09:28.974139+0800 NStimer[2560:665239] print a line ----
2017-08-29 17:09:29.974251+0800 NStimer[2560:665239] print a line ----

** Case Three:**
假设我们使用UITrackingRunLoopMode,运行demo,发现没有任何打印,当手指活动textview的时候,打印了!这是因为:UITrackingRunLoopMode只会被用户交互事件激活,而我们的时钟事件现在添加在这种模式下。

– (void)viewDidLoad {
[super viewDidLoad];
//You must add the new timer to a run loop
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(printSth) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
}
-(void)printSth
{
NSLog(@”print a line —- “);
}

Case Four
如果我们想让上下滑动TextView时打印,又想撒手不上下滑动TextView时也打印,那么就要用NSRunLoopCommonModes占位模式,这样占了两个模式,但是苹果建议用NSDefaultRunLoopMode

NSRunLoopCommonModes 这个模式官文上是这样解释的:
Objects added to a run loop using this value as the mode are monitored by all run loop modes that have been declared as a member of the set of “common” modes; see the description of CFRunLoopAddCommonMode for details.

使用此值作为模式添加到运行循环中的对象将被已声明为该组“常用”模式的成员的所有运行循环模式进行监视;有关详细信息,请参阅CFRunLoopAddCommonMode的说明。
比较拗口,意思就是:这个timer如果使用这个模式,那么在“常用”模式的子成员模式下都生效。UITrackingRunLoopMode和kCFRunLoopDefaultMode都被标记为了common模式,所以只需要将timer的模式设置为forMode:NSRunLoopCommonModes,就可以在默认模式和追踪模式都能够运行。(连接:这里写链接内容

然额,



**Case Five**
那么我们考虑下用子线程来解决:

> //    Mode:runloop在同一时间只能处理一个模式下的事件,当处理用户交互模式时,就不会处理时钟事件模式了
//    NSRunLoopCommonModes:他不是一个真正的模式,是一个占位模式
//    UITrackingRunLoopMode:用户交互模式,只要有触摸,马上处理
//    NSDefaultRunLoopMode:默认模式,网络/timer事件放在这里
  • (void)viewDidLoad {
    [super viewDidLoad];
    //You must add the new timer to a run loop
    NSThread *thread = [[NSThread alloc]initWithBlock:^{

    NSTimer *timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:@selector(printSth) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:**NSDefaultRunLoopMode**];
    *NSLog(@"end of the block");*
    

    }];
    [thread start];
    }
    -(void)printSth
    {
    NSLog(@”print a line “);
    }


> //    - (void)start;方法,官文的描述如下:
//    This method asynchronously spawns the new thread and invokes the receiver’s main method on the new thread.
//    该方法异步生成新线程,并在新线程上调用接收方的主方法。

//    If this thread is the first thread detached in the application, this method posts the NSWillBecomeMultiThreadedNotification with object nil to the default notification center.
//    如果此线程是应用程序中分离的第一个线程,则此方法将具有对象nil的NSWillBecomeMultiThreadedNotification发布到默认通知中心。

我们在子线程中加了一句打印,`NSLog(@"end of the block");` 在timer的执行方法中加了一句打印,`NSLog(@"print a line  ");`**小插曲1⃣️:**
运行程序,发现,只是打印了end of the block,说明timer没有触发方法-(void)printSth,这是为什么呢?
这就牵涉到RunLoop的知识, **因为子线程的RunLoop默认是不开的,那么RunLoop负责监听事件的功能就失去意义了**,所以,在使用子线程的时候,要手动开启RunLoop,让他跑(Run)起来。
![这里写图片描述](https://img-blog.csdn.net/20170904151227840?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvWkxKOTI1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)


处理完上面的小插曲
**小插曲2⃣️**
再次运行程序,发现:`NSLog(@"print a line ---- ");` timer可以正常触发方法了。我们用的是NSDefaultRunLoopMode模式,滑动屏幕,timer也是可以正常触发的。这是因为我们使用的NSDefaultRunLoopMode是放在子线程中的,那么拖拽(上下滑动TextView)就不会影响我们的打印。

但是,子线程block中的最后一句打印:`NSLog(@"end of the block")` 没有打印,这又是为什么呢?

> runloop开启(run)后,线程(thread变量)就不会被释放了。runloop死之后,线程(thread变量)才会释放,这里是死循环,跑起来跑到海枯石烂了。ps:这句话引自曾经听过的一个直播视频课程。
> 所以,技巧一:关掉Runloop,让RunLoop不循环,线程就释放了(间接干掉线程)。
> 或者,技巧二:直接干掉线程

**先看第一种方式:关掉Runloop,让RunLoop不循环,线程就释放了**
我们自己定义的类MyThread继承自系统的NSThread,只在MyThread.m中实现:

> -(void)dealloc{
    NSLog(@"MyThread dealloc ");
}
把之前的代码改为:
![这里写图片描述](https://img-blog.csdn.net/20170830094650162?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvWkxKOTI1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

这里的代码没有做改动:
-(void)printSth
{
NSLog(@”print a line —- “);
}
“`

运行代码,发现每隔一秒中正常打印:

2017-08-30 09:40:50.570322+0800 NStimer[2823:732917] print a line —-
2017-08-30 09:40:50.666825+0800 NStimer[2823:732917] print a line —-
2017-08-30 09:40:50.766841+0800 NStimer[2823:732917] print a line —-
2017-08-30 09:40:50.868715+0800 NStimer[2823:732917] print a line —-
2017-08-30 09:40:50.966965+0800 NStimer[2823:732917] print a line —-
2017-08-30 09:40:51.081222+0800 NStimer[2823:732917] print a line —- (此时,点击了下屏幕空白处,导致_isFinished = YES)
2017-08-30 09:40:51.093408+0800 NStimer[2823:732917] end of the block— (那么,由于我们的RunLoop的运行方式是:[[NSRunLoop currentRunLoop]**runUntilDate**:[NSDate dateWithTimeIntervalSinceNow:0.001]];) 所以,我们没有开启RunLoop
2017-08-30 09:40:51.094812+0800 NStimer[2823:732917] MyThread dealloc 子线程执行完毕后,调用了dealloc方法

tips:我们来解释下下面这个方法,看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值