RunLoop

1.RunLoop基本作用:
保持程序持续运行,程序一启动就会开一个主线程,主线程一开起来就会跑一个主线程对应的RunLoop,RunLoop保证主线程不会被销毁,也就保证了程序的持续运行
处理App中的各种事件(比如:触摸事件,定时器事件,Selector事件等)
节省CPU资源,提高程序性能,程序运行起来时,当什么操作都没有做的时候,RunLoop就告诉CUP,现在没有事情做,我要去休息,这时CUP就会将其资源释放出来去做其他的事情,当有事情做的时候RunLoop就会立马起来去做事情
2.什么时间创建
1)主线程的RunLoop
在UIApplicationMain函数中,开启了一个和主线程相关的RunLoop,导致UIApplicationMain不会返回,一直在运行中,也就保证了程序的持续运行。
2)子线程的RunLoop
RunLoop在第一次获取时创建,在线程结束时销毁,即懒加载

    每条线程都有唯一的一个与之对应的RunLoop对象
    RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
    主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
    RunLoop在第一次获取时创建,在线程结束时销毁

注:子线程执行完线程任务后会退出,不会因为有强引用或引用计数就不清空,执行RunLoop会控制子线程不退出,而RunLoop会在内部Source为空时退出RunLoop

3.线程和 RunLoop 之间是一一对应的,其关系是保存在一个 Dictionary 里。所以我们创建子线程RunLoop时,只需在子线程中获取当前线程的RunLoop对象即可[NSRunLoop currentRunLoop];如果不获取,那子线程就不会创建与之相关联的RunLoop,并且只能在一个线程的内部获取其 RunLoop
[NSRunLoop currentRunLoop];方法调用时,会先看一下字典里有没有存子线程相对用的RunLoop,如果有则直接返回RunLoop,如果没有则会创建一个,并将与之对应的子线程存入字典中(懒加载)。当线程结束时,RunLoop会被销毁
4.内部结构
一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source、Timer、Observer
每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode
如果需要切换Mode,只能退出RunLoop,再重新指定一个Mode进入,这样做主要是为了分隔开不同组的Source、Timer、Observer,让其互不影响。如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
一种Mode中可以有多个Source(事件源,输入源,基于端口事件源例键盘触摸等) Observer(观察者,观察当前RunLoop运行状态) 和Timer(定时器事件源)。但是必须至少有一个Source或者Timer,因为如果Mode为空,RunLoop运行到空模式不会进行空转,就会立刻退出,创建子线程相关的RunLoop,在子线程中创建即可,并且RunLoop中要至少有一个Timer 或 一个Source 保证RunLoop不会因为空转而退出,因此在创建的时候直接加入,如果没有加入Timer或者Source,或者只加入一个监听者,运行程序会崩溃。
1)mode

  1. kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
  2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用,会切换到kCFRunLoopDefaultMode
  4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
  5. kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用,并不是一种真正的Mode
    2)Source0/Source1/Timer/Observer
    Source0:非基于Port的 用于用户主动触发的事件(点击button 或点击屏幕)
    Source1:基于Port的 通过内核和其他线程相互发送消息(与内核相关)
    Observer:是观察者,能够监听RunLoop的状态改变
    Timer:即定时器
    5.定时器深入解析
    原来我们学到的是定时器优先级没有scroll滚动优先级高,即没有页面刷新优先级高,所以要提高其优先级,但事实并非如此,因为模式进行了切换。
    这个涉及到mode的kCFRunLoopCommonModes模式:
    1)把定时器添加到RunLoop中,并且选择默认运行模式NSDefaultRunLoopMode = kCFRunLoopDefaultMode
    2)当scoll滑动的时候,RunLoop的Mode会自动切换成UITrackingRunLoopMode模式,因此timer失效,当停止滑动,RunLoop又会切换回NSDefaultRunLoopMode模式,因此timer又会重新启动了
    3)如果将timer添加到UITrackingRunLoopMode模式中,此时只有我们在滑动textField时timer才会运行
    4)在两个模式下都添加timer 是可以的,但是timer添加了两次,并不是同一个timer
    5) 使用站位的运行模式 NSRunLoopCommonModes标记,凡是被打上NSRunLoopCommonModes标记的都可以运行,下面两种模式被打上标签
    UITrackingRunLoopMode
    kCFRunLoopDefaultMode
    6)因此也就是说如果我们使用NSRunLoopCommonModes,timer可以在UITrackingRunLoopMode,kCFRunLoopDefaultMode两种模式下运行
[[NSRunLoop  mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

7)同样还有performSelector默认是在default模式下运行,因此在滑动ScrollView时,方法不会执行

 [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"abc"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode,UITrackingRunLoopMode]];

6.内存管理
Timer和Source也是一些变量,需要占用一部分存储空间,所以要释放掉,如果不释放掉,就会一直积累,占用的内存也就越来越大,这显然不是我们想要的。那么什么时候释放,怎么释放呢?
RunLoop内部有一个自动释放池,当RunLoop开启时,就会自动创建一个自动释放池,当RunLoop在休息之前会释放掉自动释放池的东西,然后重新创建一个新的空的自动释放池,当RunLoop被唤醒重新开始跑圈时,Timer,Source等新的事件就会放到新的自动释放池中,当RunLoop退出的时候也会被释放。
注意:只有主线程的RunLoop会默认启动。也就意味着会自动创建自动释放池,子线程需要在线程调度方法中手动添加自动释放池

7.总结
在这里插入图片描述
源码地址:https://opensource.apple.com/tarballs/CF/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值