草庐IT

一、RunLoop初体验~思考

LonelyBanana 2023-03-28 原文
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"%ld",(long)count++);
    }];

[self.view addSubview:self.tv_list];

写了个UITableView然后我添加个NSTimer,接下来回发生什么事情呢?
当滑动UITableView时,NSTimer计时器就会停止。
相信绝大多数开发者都遇到过这个问题,当然百度一下会有很多答案。

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

添加这代码就完美的解决了这个问题,但是为什么会这样呢?
猜测肯定跟NSRunLoop有关。那NSRunLoop是什么?

1、NSRunLoop是什么?

截屏2022-07-04 14.21.09.png

NSRunLoop对象处理源的输入,例如来自窗口系统和NSPort对象的鼠标和键盘事件。NSRunLoop对象也处理NSTimer事件。

你的应用程序既不创建也不显式管理NSRunLoop对象。系统根据需要为每个NSThread对象创建一个NSRunLoop对象,包括应用程序的主线程。如果您需要访问当前线程的运行循环,请使用类方法currentRunLoop

注意,从NSRunLoop的角度来看,NSTimer对象不是“输入”——它们是一种特殊的类型,当它们触发运行循环时,它们不会导致运行循环返回。

似乎也看不明白说的什么意思。
通俗易懂的话这玩意是个死循环,在有事件响应的时候,RunLoop去处可以响应处理一些事件,没有事件响应的时候RunLoop会进入休眠,并且他和线程是一一对应的。一个线程里面有的话只可以拥有一个RunLoop。

2、NSRunLoopMode是什么?

035a7f84ea33411db674f75f9538fb92_tplv-k3u1fbpfcp-watermark.png

Modes that a run loop operates in. 各位自行翻译吧。

1264209451244f0aa91229e7c03f445f_tplv-k3u1fbpfcp-zoom-in-crop-mark_3024_0_0_0.jpg

目前NSRunLoopMode有这5种,但是只有NSDefaultRunLoopMode UITrackingRunLoopModel NSRunLoopCommonModes这三种是用于iOS上面的,剩下两种是用于MacOS上的。

NSDefaultRunLoopMode

c8c2dc621768408783c77c26ec85cb75_tplv-k3u1fbpfcp-zoom-in-crop-mark_3024_0_0_0.jpg

设置为处理连接对象以外的输入源的模式。

UITrackingRunLoopModel

467fea02a857496caefc190c009d3f83_tplv-k3u1fbpfcp-watermark.png

在控件中进行跟踪时设置的模式。

NSRunLoopCommonModes

e85886ffe56e43ccbe26d4c4736ebc30_tplv-k3u1fbpfcp-watermark.png

包含一个或多个其他运行循环模式的伪模式。
其实这个伪模式中包含了NSDefaultRunLoopMode|UITrackingRunLoopModel这两种模式

3、测试

__block NSInteger count = 0;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
    NSLog(@"%ld",(long)count++);
    //添加这行代码
    NSLog(@"Timer的currentModel=%@",[[NSRunLoop currentRunLoop] currentMode]);
    }];
**RunLoopTest[58445:4388936] Timer的currentModel=kCFRunLoopDefaultMode**

NSTimer在运行过程中打印出来的Mode为kCFRunLoopDefaultMode这个mode其实这个mode就等于NSDefaultRunLoopMode 因为NSRunLoop是基于CKRunLoop开发的。所以NSTimer在计时时RunLoop的Model为NSDefaultRunLoopMode

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    NSLog(@"TableView滑动---%@", [[NSRunLoop currentRunLoop] currentMode]);
}

接下来在scrollViewDidScroll这个代理方法里面添加上滑动监听

**RunLoopTest[58641:4397470] TableView滑动---UITrackingRunLoopMode**

所以基本上可以分析出来为什么在UITableView滑动的时候NSTimer计时不起作用,因为在UITableView滑动的时候当前主线程的RunLoop的Mode切换到了UITrackingRunLoopMode这个模式但是NSTimer只在NSDefaultRunLoopMode做响应,当UITableView停止的时候RunLoop的Mode又会切回到NSDefaultRunLoopMode,所以如果把NSTimer在当前RunLoop中的模式添加为NSRunLoopCommonModes那其实可以理解为NSTimer可以在NSDefaultRunLoopMode | UITrackingRunLoopModel这两种模式中都起作用。

当添加了[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];这行代码后:

**RunLoopTest[80618:4452646] 0**
**RunLoopTest[80618:4452646] Timer的currentModel=kCFRunLoopDefaultMode**
**RunLoopTest[80618:4452646] 1**
**RunLoopTest[80618:4452646] Timer的currentModel=kCFRunLoopDefaultMode**
**RunLoopTest[80618:4452646] TableView滑动---UITrackingRunLoopMode**
**RunLoopTest[80618:4452646] 2**
**RunLoopTest[80618:4452646] Timer的currentModel=UITrackingRunLoopMode**

可以发现当UITableView滑动的时候确实Timer的Mode变为了UITrackingRunLoopMode

接下来我如果把Timer的Mode直接添加成UITrackingRunLoopMode是否可行呢?

//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
**RunLoopTest[80891:4465038] 0**
**RunLoopTest[80891:4465038] Timer的currentModel=kCFRunLoopDefaultMode**
**RunLoopTest[80891:4465038] TableView滑动---UITrackingRunLoopMode**
**RunLoopTest[80891:4465038] TableView滑动---UITrackingRunLoopMode**
**RunLoopTest[80891:4465038] 1**
**RunLoopTest[80891:4465038] Timer的currentModel=UITrackingRunLoopMode**

证明也是可以一变滑动一遍计时,效果是一样的。

更多的思考

上面提到了一个线程只会对应一个RunLoop,那如果在开辟一个线程去做计时但是不添加Timer的Model是否可以实现一样的效果呢?

- (void)viewDidLoad {
    [super viewDidLoad];
    //开启线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(startTimer) object:nil];
    [thread start];
    [self.view addSubview:self.tv_list];
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

}
-(void)startTimer{
    __block NSInteger count = 0;
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"%ld",(long)count++);
        NSLog(@"Timer的currentModel=%@",[[NSRunLoop currentRunLoop] currentMode]);
    }];
}

创建了这段代码,但是发现Timer根本没有执行。这是因为什么呢?
原因是因为主线程的NSRunLoop是系统自己创建的所以不需要我们去创建它,但是我们创建的子线程是没有NSRunLoop的所以需要我们手动的去创建它才行。
添加这行代码[[NSRunLoop currentRunLoop] run];

- (void)viewDidLoad {
    [super viewDidLoad];
    //开启线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(startTimer) object:nil];
    [thread start];
    [self.view addSubview:self.tv_list];
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

}
-(void)startTimer{
    __block NSInteger count = 0;
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"%ld",(long)count++);
        NSLog(@"Timer的currentModel=%@",[[NSRunLoop currentRunLoop] currentMode]);
    }];
    //创建RunLoop
    [[NSRunLoop currentRunLoop] run];
}

这样NSTimer正常的跑了起来。

结论:
所以经过上面的时间得出NSTimer在UITableView滑动时无法工作是因为NSTimer工作是依靠RunLoop,而UITableView的滑动响应也是依靠RunLoop,但是NSTimer默认是在NSDefaultRunLoopMode模式下去触发响应的,而UITableView的滑动时主线程的RunLoop会切换到UITrackingRunLoopMode模式,所以NSTimer无法在UITableView滑动的时候工作,解决办法比较简单地就是给NSTimer添加NSRunLoopCommonModes这种伪模式或者直接添加UITrackingRunLoopMode模式。在本文中提到了创建一个子线程并且给子线程添加RunLoop去解决NSTimer滑动计时的问题,作者这里不推荐这种方式来实现,因为在实际开发中很容易出现循环引用的问题,次方法只是为了测试下子线程的RunLoop,没必要画蛇添足去解决这种简单的问题。

有关一、RunLoop初体验~思考的更多相关文章

  1. 西安华为OD面试体验 - 2

    西安华为OD面试体验开始投简历技术面试进展工作进展开始投简历去年一整年一直在考研和工作之间纠结,感觉自己的状态好像当时的疫情一样差劲。之前刚毕业的时候投了个大厂的简历,结果一面写算法的时候太拉跨了,虽然知道时dfs但是代码熟练度不够,放在平时给足时间自己可以调试通过,但是熟练度不够那面试当时就写不出来被刷了。说真的算法学到后期我感觉最重要的是熟练度和背板子(对于我这种普通玩家来说),面试题如果一上来短时间内想不出思路就完蛋了。然后由于当时找的工作不是很理想就又想考研了。但是考研是有风险的,我自我感觉自己可能冲不上那个学校,而找工作一个没成可以继续找嘛。本着抱着试试看的态度在boss上投了简历,

  2. 「想体验ChatGPT中文聊天?」那快进来,你用不上算我输 - 2

    ♥️作者:白日参商🤵‍♂️个人主页:白日参商主页♥️坚持分析平时学习到的项目以及学习到的软件开发知识,和大家一起努力呀!!!🎈🎈加油!加油!加油!加油🎈欢迎评论💬点赞👍🏻收藏📂加关注+!「想体验ChatGPT中文聊天?」那快进来,你用不上算我输项目场景:项目条件一、那就开始吧1、安装ChatGPT-Desktop2、OpenAPI设置二、使用实例恭喜你!!!配置成功了!!!API和URL都是博主免费提供给大家的!!!恭喜你!!!配置成功了!!!API和URL都是博主免费提供给大家的!!!🎈🎈加油!加油!加油!加油🎈欢迎评论💬点赞👍🏻收藏📂加关注+!项目场景:近几个月可以说ChatGPT是火得一

  3. 企业大数据发展面临问题之存算分离技术思考 - 2

    文章目录概述背景为何要存算分离优势**应用场景**存算分离产品技术流派华为JuiceFSHashDataXSKY概述背景Hadoop一出生就是奔存算一体设计,当时设计思想就是存储不动而计算(code也即是代码程序)动,负责调度Yarn会把计算任务尽量发到要处理数据所在的实例上,这也是与传统集中式存储最大的不同。为何当时Hadoop设计存算一体的耦合?要知道2006年服务器带宽只有100Mb/s~1Gb/s,但是HDD也即是磁盘吞吐量有50MB/s,这样带宽远远不够传输数据,网络瓶颈尤为明显,无奈之举只好把计算任务发到数据所在的位置。众观历史常言道天下分久必合合久必分,随着云计算技术的发展,数据

  4. 基于RTS超低延时直播优化强互动场景体验 - 2

    RTS在阿里云视频直播的基础上进行底层技术优化,通过集成阿里云播放器SDK,支持在千万级并发场景下节点间毫秒级延时直播的能力,弥补了传统直播存在3~6秒延时的问题,确保了超低延时、低卡顿、秒开流畅的直播观看体验。本文介绍了基于RTS超低延迟直播优化强互动场景体验的最佳实践方案,并以阿里云播放器Aliplayer为例,详细介绍RTS超低延迟拉流接入、自动降级、排障信息获取等逻辑的实现,助力企业打造互动直播行业的产品竞争力。适用场景该方案适用于对超低延迟直播有诉求的客户,尤其是业务中存在强互动场景直播的场景。强互动场景直播主要是指对主播和观众存在互动,或观众存在更高实时性观看、画面互动需求的情况,

  5. 【思考】聊聊低代码的实践之路 - 2

    文章目录背景一、最初的疑惑二、简单聊聊原理三、组织内实践案例四、实践带来的反思五、最后聊几句问题背景这个概念由来已久,但是在国内兴起,是最近几年;低代码即Low-Code;指提供可视化开发环境,可以用来创建和管理软件应用;简单的说就是可以通过各种组件的拖拽,实现页面的创建,交互流程和逻辑,以及数据层面的管理,更加高效的实现需求;早先在数据公司时;见识过低代码的应用,也参与过部分研发,比如元数据平台,BI分析等;不过,当时还是以数据管理的工具来定义项目,并非是低代码;从「2020年底」开始;实际上,那个时间节点,低代码平台的应用已经形成趋势了;现在的公司,将低代码平台的使用规划到业务体系中;后来

  6. ruby-on-rails - 用 Ruby 思考 - 2

    我目前正忙于学习Ruby和Rails,并且由于我有基于C语言的背景,Ruby的一些概念是新的并且有些陌生。对我来说特别具有挑战性的是适应处理常见问题的“Ruby方式”,因此我经常发现自己用Ruby编写C代码,这不是我想要实现的目标。想象一下有这样一个模式:ActiveRecord::Schema.define(:version=>20111119180638)docreate_table"bikes",:force=>truedo|t|t.string"Brand"t.string"model"t.text"description"endend数据库已经包含了几辆不同的自行车。我的目标

  7. 评测:对接GPT-4的NewBing使用体验变化 - 2

    GPT-4是OpenAI发布的最新一代语言模型,于2023年3月14日正式发布,并通过API和ChatGPTPlus平台向用户开放。微软也证实,在GPT-4正式发布之前,就已经在部分版本的Bing中使用GPT-4技术。本文旨在对使用GPT-4技术的NewBing进行中立、专业的使用评测,探讨其使用体验的变化。界面与限额早于2月26日小编获得Newbing试用资格,届时Newbing初体验并不理想,限制多,例如对话只能持续5条自动终止,对话字符受限,生成效果不尽如人意。然而今日对接GPT-4全新bing额度扩充至15条,且对话字数扩容至2000字。Newbing提供了三种模式选项,即创造力、平衡

  8. 腾讯云区块链服务平台(TBaaS)长安链体验网络公测试用感受 - 2

    前阵InfoQ社区看到腾讯云腾讯云区块链服务平台(TBaaS)长安链体验活动,一顿操作猛如虎报了个名,体验完用一个字概括:强。非要再加几个字的话,总体感受下来装配模式灵活高效,配套工具完整辩解。话不多说开始主题本文目录结构分为区块链分类和TBaaS平台介绍、TBaaS平台上链教程三个部分一、区块链分类:大体上来说,区块链可分为公链,联盟链,私有链三种:公有链(PublicBlockchain)公有链是指任何人都能参与的区块链。公有链是去中心化程度最高的区块链,不受机构控制,整个账本对所有人公开透明。任何人都能在公有链上查询交易、发送交易、参与记账。加入公有链不需要任何人授权,可以自由加入或者离

  9. 科大讯飞刘聪:由ChatGPT浪潮引发的深入思考与落地展望 - 2

    近期,以“生成式人工智能”(GenerativeAI)为核心技术的聊天机器人ChatGPT火爆全球。百度、阿里巴巴、科大讯飞、360等国内企业纷纷抛出ChatGPT相关进展,打造中国版的ChatGPT。科大讯飞此前在投资者互动平台表示,ChatGPT主要涉及到自然语言处理相关技术,属于认知智能领域的应用之一,公司在该方向技术和应用具备长期深厚的积累。并称2022年12月已进一步启动生成式预训练大模型任务攻关,类ChatGPT技术将在今年5月率先落地科大讯飞AI学习机产品。近日,科大讯飞副总裁、研究院执行院长刘聪围绕什么是ChatGPT,它强在哪里?会对未来世界带来哪些颠覆性影响?进一步阐述Ch

  10. ruby - 如何停止思考 "relationally" - 2

    在工作中,我们最近开始了一个使用CouchDB(面向文档的数据库)的项目。我一直很难摆脱我所有的关系数据库知识。我想知道你们中的一些人是如何克服这个障碍的?您是如何停止关系式思考并开始以文件方式思考的(我为编造这个词而道歉)。有什么建议吗?有帮助的提示?编辑:如果有任何不同,我们正在使用Ruby和CouchPotato连接到数据库。编辑2:太麻烦我接受答案了。我认为,我选择了对我学习帮助最大的那个。但是,我想没有真正的“正确”答案。 最佳答案 我认为,在仔细阅读了关于这个主题的几页之后,这完全取决于您正在处理的数据类型。RDBMS代

随机推荐