IOS开发入门之RunLoop 总结:RunLoop的应用场景(二)
凌雪 2018-11-09 来源 :网络 阅读 948 评论 0

摘要:本文将带你了解IOS开发入门RunLoop 总结:RunLoop的应用场景(二),希望本文对大家学IOS有所帮助。

本文将带你了解IOS开发入门RunLoop 总结:RunLoop的应用场景(二),希望本文对大家学IOS有所帮助。


       

RunLoop   总结:RunLoop的应用场景(二)


参考资料

好的书籍都是值得反复看的,那好的文章,好的资料也值得我们反复看。我们在不同的阶段来相同的文章或资料或书籍都能有不同的收获,那它就是好文章,好书籍,好资料。
关于iOS   中的RunLoop资料非常的少,以下资料都是非常好的。

CF框架源码(这是一份很重要的源码,可以看到CF框架的每一次迭代,我们可以下载最新的版本来分析,或与以下文章对比学习。目前最新的是CF-1153.18.tar.gz)RunLoop官方文档(学习iOS的任何技术,官方文档都是入门或深入的极好手册;我们也可以在Xcode—>Help—>Docementation   and API Reference —>搜索RunLoop—> Guides(59)—>《Threading   Programming Guide:Run   Loops》这篇即是)深入理解RunLoop(不要看到右边滚动条很长,其实文章占篇幅2/5左右,下面有很多的评论,可见这篇文章的火热)RunLoop个人小结   (这是一篇总结的很通俗容易理解的文章)sunnyxx线下分享RunLoop(这是一份关于线下分享与讨论RunLoop的视频,备用地址:https://pan.baidu.com/s/1pLm4Vf9)iPhonedevwiki中的CFRunLoop(commonModes中其实包含了三种Mode,我们通常知道两种,还有一种是啥,你知道么?)维基百科中的Event   loop(可以看看这篇文章了解一下事件循环)

使用场景

1.我们经常会在应用中看到tableView 的header   上是一个横向ScrollView,一般我们使用NSTimer,每隔几秒切换一张图片。可是当我们滑动tableView的时候,顶部的scollView并不会切换图片,这可怎么办呢?
2.界面上除了有tableView,还有显示倒计时的Label,当我们在滑动tableView时,倒计时就停止了,这又该怎么办呢?

场景中的代码实现

我们的定时器Timer是怎么写的呢?
一般的做法是,在主线程(可能是某控制器的viewDidLoad方法)中,创建Timer。
可能会有两种写法,但是都有上面的问题,下面先看下Timer的两种写法:


   

<code>// 第一种写法

NSTimer   *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerUpdate) userInfo:nil   repeats:YES];

[[NSRunLoop   currentRunLoop] addTimer:timer   forMode:NSDefaultRunLoopMode];

[timer fire];

//   第二种写法

[NSTimer   scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerUpdate) userInfo:nil   repeats:YES];</code>

   

上面的两种写法其实是等价的。第二种写法,默认也是将timer添加到NSDefaultRunLoopMode下的,并且会自动fire。。
要验证这一结论,我们只需要在timerUpdate方法中,将当前runLoop的currentMode打印出来即可。


   

<code><code>-   (void)timerUpdate

{

    NSLog(@"当前线程:%@",[NSThread currentThread]);

    NSLog(@"启动RunLoop后--%@",[NSRunLoop   currentRunLoop].currentMode);

//      NSLog(@"currentRunLoop:%@",[NSRunLoop   currentRunLoop]);

    dispatch_async(dispatch_get_main_queue(),   ^{

        self.count   ++;

        NSString *timerText = [NSString stringWithFormat:@"计时器:%ld",self.count];

        self.timerLabel.text =   timerText;

    });

}

//   控制台输出结果:

2016-12-02   15:33:57.829 RunLoopDemo02[6698:541533]   当前线程:<nsthread: 0x600000065500="">{number = 1, name   = main}

2016-12-02 15:33:57.829 RunLoopDemo02[6698:541533]   启动RunLoop后--kCFRunLoopDefaultMode</nsthread:></code></code>

   

然后,我们在滑动tableView的时候timerUpdate方法,并不会调用。
*   原因是啥呢?*
原因是当我们滑动scrollView时,主线程的RunLoop   会切换到UITrackingRunLoopMode这个Mode,执行的也是UITrackingRunLoopMode下的任务(Mode中的item),而timer   是添加在NSDefaultRunLoopMode下的,所以timer任务并不会执行,只有当UITrackingRunLoopMode的任务执行完毕,runloop切换到NSDefaultRunLoopMode后,才会继续执行timer。

*   要如何解决这一问题呢?*
解决方法很简单,我们只需要在添加timer 时,将mode   设置为NSRunLoopCommonModes即可。


<code><code><code><code><code><code>-   (void)timerTest

{

    // 第一种写法

    NSTimer *timer = [NSTimer   timerWithTimeInterval:1.0 target:self selector:@selector(timerUpdate) userInfo:nil   repeats:YES];

    [[NSRunLoop currentRunLoop] addTimer:timer   forMode:NSRunLoopCommonModes];

    [timer fire];

    //   第二种写法,因为是固定添加到defaultMode中,就不要用了

}</code></code></code></code></code></code>

   

从RunLoop官方文档和   iPhonedevwiki中的CFRunLoop可以看出,NSRunLoopCommonModes并不是一种Mode,而是一种特殊的标记,关联的有一个set   ,官方文档说:For Cocoa applications, this set includes the default,   modal, and event tracking modes by   default.(默认包含NSDefaultRunLoopMode、NSModalPanelRunLoopMode、NSEventTrackingRunLoopMode)
添加到NSRunLoopCommonModes中的还没有执行的任务,会在mode切换时,再次添加到当前的mode中,这样就能保证不管当前runloop切换到哪一个mode,任务都能正常执行。并且被添加到NSRunLoopCommonModes中的任务会存储在runloop   的commonModeItems中。

其他一些关于timer的坑

我们在子线程中使用timer,也可以解决上面的问题,但是需要注意的是把timer加入到当前runloop后,必须让runloop   运行起来,否则timer仅执行一次。

示例代码:




   

<code><code><code><code><code><code><code><code><code>//首先是创建一个子线程

- (void)createThread

{

    NSThread *subThread = [[NSThread alloc]   initWithTarget:self selector:@selector(timerTest) object:nil];

    [subThread   start];

    self.subThread =   subThread;

}

 

//   创建timer,并添加到runloop的mode中

-   (void)timerTest

{

    @autoreleasepool {

        NSRunLoop *runLoop = [NSRunLoop   currentRunLoop];

        NSLog(@"启动RunLoop前--%@",runLoop.currentMode);

        NSLog(@"currentRunLoop:%@",[NSRunLoop currentRunLoop]);

        //   第一种写法,改正前

    //    NSTimer   *timer = [NSTimer timerWithTimeInterval:1.0 target:self   selector:@selector(timerUpdate) userInfo:nil   repeats:YES];

    //      [[NSRunLoop currentRunLoop] addTimer:timer   forMode:NSDefaultRunLoopMode];

    //    [timer   fire];

        // 第二种写法

      [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerUpdate) userInfo:nil   repeats:YES];

 

        [[NSRunLoop currentRunLoop]   run];

    }

}

 

//更新label

-   (void)timerUpdate

{

    NSLog(@"当前线程:%@",[NSThread currentThread]);

    NSLog(@"启动RunLoop后--%@",[NSRunLoop   currentRunLoop].currentMode);

    NSLog(@"currentRunLoop:%@",[NSRunLoop   currentRunLoop]);

    dispatch_async(dispatch_get_main_queue(),   ^{

        self.count ++;

        NSString *timerText = [NSString   stringWithFormat:@"计时器:%ld",self.count];

        self.timerLabel.text =   timerText;

    });

}

</code></code></code></code></code></code></code></code></code>

   

添加timer   前的控制台输出:

添加timer后的控制台输出:

从控制台输出可以看出,timer确实被添加到NSDefaultRunLoopMode中了。可是添加到子线程中的NSDefaultRunLoZ喎?"/kf/ware/vc/"   target="_blank">vcE1vZGXA76Oszt7C28jnus659ravo6x0aW1lcra8xNy5u7rc1f2zo7XE1MvXqqGj1eLT1srHzqrJtsTYo788L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvcD4NCjxwPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPtXivs3Kx7bgz9+zzNPrcnVubG9vcLXEudjPtcHLo6zDv9K7uPbP37PMtrzT0NK7uPbT69auudjBqrXEUnVuTG9vcKOstvjDv9K7uPZSdW5Mb29wv8nE3Lvh09C24Lj2TW9kZaGjQ1BVu+HU2rbguPbP37PMvOTH0Lu7wLTWtNDQyM7O8aOss8rP1rP2tuC49s/fs8zNrMqx1rTQ0LXE0Ke5+6Gj1rTQ0LXEyM7O8cbkyrW+zcrHUnVuTG9vcMiluPe49k1vZGXA79a00NC497j2aXRlbaGj0vLOqlJ1bkxvb3DKx7bAwaK1xMG9uPajrM/gu6Wyu7vh07DP7KOsy/nS1NTa19PP37PMzO2803RpbWVyo6y7rLavytPNvMqxo6x0aW1lcsTc1f2zo9TL0NChozwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L2NvZGU+PC9wPg0KPGgyIGlkPQ=="总结">总结

1、如果是在主线程中运行timer,想要timer在某界面有视图滚动时,依然能正常运转,那么将timer添加到RunLoop中时,就需要设置mode   为NSRunLoopCommonModes。
2、如果是在子线程中运行timer,那么将timer添加到RunLoop中后,Mode设置为NSDefaultRunLoopMode或NSRunLoopCommonModes均可,但是需要保证RunLoop在运行,且其中有任务。

    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程