runloop 笔记

来源:互联网 发布:cad迷你画图软件 编辑:程序博客网 时间:2024/06/06 05:49

1.使程序一直运行并接受用户输入
2.决定程序在何时应该处理那些Event
3.调用解耦(Message Queue)
4.节省CPU时间

主线程几乎所有的函数都从以下六个之一的函数调用器

    static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__();      static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__();      static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__();      static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__();      static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__();      static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__();  

CFRunloop 和Thread 一一绑定。

CFRunloopMode —-
CFRunLoopSource - CFRunLoopTimer – CFRunLoopObeserver

这里写图片描述

RunLoopTimer 的封装

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;//延时执行- (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;//刷新频率 和系统ui 刷新率一样- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode;

Source 是Runloop的数据源的抽象类(Protocol)实际他这个就能用了
RunLoop 定义了两个Version的Source:
1.Source0:处理App内部事件、App自己负责管理(触发)、如UIEvent、CFSocket(可以打断点看)
2.Source1:由RunLoop和内核管理、Mach port(轻量级进程间通讯的一种方式)驱动、如CFMach port 、CFMessagePort(系统用Source1 实现的)。认为是进程间的端口,往进程发消息,就是往进程的端口发消息。端口被监听的话就会收到消息。

observer 枚举
entry 进入 before time before source 将要执行 before wait 将要sleep睡眠了 after wait 我被唤醒了 exit 退出

CAAnimation 用了这个不会代码一执行就跑 在before wait 和after wait 才做动画操作。保证所有信息都拿到了

autorelease pool对象到底什么时候释放:
在runloop 处于observer 触发的时候释放的。

runloop 在同一时间只能并且必须在一种特定Mode 下Run
更换Mode 时候,需要停止当前的Mode,然后重启新的Loop
mode 是ios APP滑动顺畅的关键

NSDefaultRunLoopMode(不滑动。默认空闲的mode)UITrackingRunLoopMode(滑动mode)UIInitializationRunLoopMode(私有,启动到显示ui之间出现的)NSRunLoopCommonModes(自己定义 或者 默认某些mode 的集合 )( 默认 为NSDefaultRunLoopMode+ UITrackingRunLoopMode)(可以自己给mode 添加某些模型)

经典问题: nstimer

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;timer 会自己添加一个mode+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;受到滑动事件的影响。需要给timer 增加一个commonmode 才可以滑动时显示timer数据- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;

滑动的时候

runloop会从default 转换成tracking滑动 在滑动将要结束时候再转换为 default模式

和GCD的关系

只和get main queue 有关 因为主线程唯一性
gcd 中分发到主线程 就是转交给runloop处理

空闲时刻等待 和唤醒

睡眠状态和mach-port 有关
一.会指定一个用于唤醒的端口,发送一个消息给内核,
让内核处于接收状态,就变成处于等待状态了(sleep)
等待内核唤醒

二.调用mach_msg 监听唤醒端口、
被唤醒前、系统内核将这个线程挂起、停留在mach_msg_trap状态
(是开放的,可以调用)

三、由另外一个线程(或另外一个进程中的某个线程) 向内核发送这个
端口的msg后,trap状态被唤醒。Runloop 继续开始干活

Runloop的执行顺序 do while:
1.设置过期时间 不能出现死循环 gcd timeout
2.告知obesrve状态: before time before source
3.Do blocks Do sources0(看有没有消息)
4. check GCD(有没有调用主线程的东西需要Runloop去处理)
5. 告知obeserve 处于 before waiting状态了
6. 发出端口号—-变为睡眠状态
7. ——————接下来是漫长的等待,直到有唤醒为止————–
8. 接收到了唤醒消息
9. 告诉obeserver 处于 after watiting 状态了
10. 开始处理事件
11. 如果是timer 唤醒的—- 就处理timer。多个timer 就排序。。。cfrunloop等
12. 如果是主线程gcd唤醒的— 就去处理gcd
13. 都不是的话—- 就是source 1(基于port的)—- (比如说NSURLConnection网络某个端口来数据了)
15. 如果没有接收到超时 和 外部干掉了runloop 就会循环这个

runloop 实践
1.AFN 旧版本怎么用runloop创建一个常驻线程的

-(void)networkRequestThreadEntryPoint:(id)__unused object{    @autoreleasepool {        [[NSThread currentThread] setName:@"AFNetworking"];        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];//起一个监听的作用,保证runloop不会退出        [runLoop run];    }}- (NSThread *)netwoekRequestThread {    static NSThread *_networkRequestThread = nil;    static dispatch_once_t oncePredicate;    dispatch_once(&oncePredicate,^{        _netWorkRequestThread =         [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];        [_netWorkRequestThread start];    });    returen _netWorkRequestThread;}

语音就可以模仿这个.按住说话,设置一个常驻线程,
平常啥也不干,到需要说话的时候,就把语音唤醒。

2.设置图片,防止图片滑动的时候卡,不需要判断是否处于滑动状态了

[self.avatarImageView performSelector:@selector@(setImage:) withObject:downLoadImage afterDelay:0 inModes:@[NSDefaultRunLoopMode]];

3.接到Crash 的siganal 后手动重启RunLoop。弹出友好提示。

CFRunLoopRef runLoop = CFRunLoopGetCurrent();NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runLoop));while(1) {    for (NSString *mode in AllModes) {        CFRunLoopRunInMode((CFStringRef)mode,0.001,false);    }}

4.异步test case

-(void)runUntilBlock:(BOOL(^)())blcok tineout:(NSTimrInterval)timeout {    NSDate *timeOutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];    do {        CFTimeInterval quantum = 0.0001;        CFRunLoopInMode(KCFRunLoopDefaultMode,quantum,false);    }while{[timeoutDate timeIntervalSinceNow]>0.0 && !block()};    //每0.0001秒验证}//改成runloop sleep 之前验证....太长不抄了

参考文章
iOS刨根问底-深入理解RunLoop
一个tableview滑动优化
RunLoop总结:RunLoop的应用场景(三)滚动视图流畅性优化

原创粉丝点击