RunLoop
Event驱动 主体就是一个死循环,没事-休眠,有事-唤醒-执行
runloop:用于解决类似你滑动了屏幕产生了多个事件,为了解耦不影响用户操作,将这些事件加入一个消息队列,这边就不用再去管理这些事件,不会影响操作也不会影响事件的执行,它会从队列中取出一个一个的执行。主线程不需要等待事件处理完成才能进行下一步操作。
1、使程序一直运行并接受用户输入
2、决定程序在何时应该处理哪些Event
3、调用解耦(Message Queue)
4、节省cpu时间,runLoop会造成类似的死循环,使CPU一直在运行,但是它可以休眠在没事的时候不会让cpu运行的机制。
NSTimer依赖与runloop实现。
UIEvent从产生到分发都是通过runloop跑的。
Autorelease与runloop有关
CAdisplayLink 是每画一贞60ps或者30ps就回调一次。
CATransition 动画效果,push等改变frame等等的效果
CAAnimation UIView的动画效果
NSURLConnection AFNetworking delegate和网络回来的数据都是通过runloop跑的
断点时 会看到调用堆栈 (如 Thread1)堆栈里面的很多东西都是有runloop搞出来的,看的时候从底下往上看。大部分堆栈都会看到start到runloop 上面的调用可能不一样。
主线程或者含有runloop的线程都从一下六个之一的函数调起:
source0:处理App内部事件,app自己负责管理(触发),如UIEvent,CFSocket
source1:由runloop和内核管理,Mach port驱动,如CFMachPort(NSPort是对它的封装)、CFMessagePort
runloop构成元素:
UIkit通过RunLoopObserver在Runloop两次sleep对AutoreleasePool进行pop和push将这次Loop 中产生的Autorelease对象释放。
RunLoop在同一时间只能且必须在一种特定的Mode下Run
更换Mode时,需要停止当前loop,然后重启loop。
Mode是iOS app滑动门顺畅的关键
CFRunLoopMode:
NSDefaultRunLoopMode 默认状态,空闲状态,程序起来切换到这个mode,不滑动就一直是这个mode
UITrackingRunLoopMode 滑动scrollView时的Mode
UIInitializationRunLoopMode 私有 app启动时的Mode
NSRunLoopCommonModes(结构类似数组)mode集合
NSTimer:不管是重复性的timer还是一次性的timer都会对它的方法的接收者进行retain,这两种timer的区别在于“一次性的timer在完成调用以后会自动将自己invalidate,而重复的timer则将永生,直到你显示的invalidate它为止”。
timer对它的接收者进行retain,从而保证了timer调用时的正确性,但是又引入了接收者的内存管理问题。特别是对于重复性的timer,它所引用的对象将一直存在,将会造成内存泄露。
对于重复的timer一定要在特定的时候调用invalidate使它失效,因为repeats为YES时会对它的接受者(self)造成强引用而无法释放造成内存泄漏。必须在viewWillDisappear的时候,将计数器timer停止,否则可能会导致内存泄露。
- //取消定时器 之后一定要将定时器置为空,否则还是没有释放。
- [timer invalidate];
- timer = nil;
要想实现:先停止,然后再某种情况下再次开启运行timer,可以使用下面的方法:
- //关闭定时器
- [myTimer setFireDate:[NSDate distantFuture]];
- //开启定时器
- [myTimer setFireDate:[NSDate distantPast]];
- 使用场景:界面消失的时候关闭定时器,显示的时候打开定时器。
NSTimer为什么要添加到RunLoop中才会有作用
前面的例子中我们使用的是一种便利方法,它其实是做了两件事:首先创建一个timer,然后将该timer添加到当前runloop的default mode中。也就是这个便利方法给我们造成了只要创建了timer就可以生效的错觉,我们当然可以自己创建timer,然后手动的把它添加到指定runloop的指定mode中去。NSTimer也是一种sourece,所有的source如果要起作用,就地加入到runloop中去,如果一个runloop没有包含任何资源,runloop会立马退出。
timer 默认添加的是defalut mode 这种mode不负责处理滑动事件,所以滑动的时候是不会执行timer的。
若是不希望timer收到scrollView的影响,需要自己创建定时器,然后添加到NSRunLoopCommonModes中。滑动的时候被切换到UITrackingRunLoopMode中,不是默认的mode,所以timer不执行。
NSTimer加到了runloop中但是不触发事件:
1、runloop是否运行。每一个线程都有自己的runloop,程序主线程会自动使runloop生效,但是对于我们自己新建的线程的runloop是不会自动运行的,需要我们自己启动runloop运行。
2、mode是否正确
想要timer生效就要将它添加到runloop的指定的mode中去,通常是加入主线程的defalut mode,但是我们这样做了为什么timer还是没有触发事件这是为什么?
这是因为timer添加的时候需要指定一个mode,因为同一线程的runloop在运行的时候只能处于一种mode。当处于timer添加的mode时候timer才能得到触发事件的机会。所以 要让timer生效,必须保证该线程的runloop已启动,而且运行的runloop的mode也要匹配。
timer在子线程中,此时线程不处于子线程,那么子线程的runloop停掉,timer也停掉。
runloop与dispatch_get_main_queue():gcd中dispatch到main queue的block被分发到main runloop执行。
RunLoop的挂起与唤醒:
按debug的暂停键能看到一个堆栈。
AFNetworking中的runloop:
TableView cell延迟加载图片的新思路:
1、UI解决方法,滑的时候不去设,停止之后去加载图片
2、runloop 在cell里面把设图片这个事 在defalut mode里面去做,滑的时候就不会去设,不会影响滑动的帧数。
让Crash的App回光返照: