博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS runloop详解
阅读量:5262 次
发布时间:2019-06-14

本文共 3973 字,大约阅读时间需要 13 分钟。

    写这篇文章开始之前,我都不知道runloop是什么东西,如果从字面的意思翻译应该是一直循环的跑,怀疑可能和死锁有关系,可是死锁具体是怎么回事,我只是记得有这个说法,也发现了一个自己不懂的知识。

 

初识runloop

    我在网上看了一下@sunnnyxx 关于runloop的视频.了解了一下runloop相关知识,也去网络上看各种关于runloop的讲述。

    我们一般程序就是执行一个线程,是一条直线,有起点终点,而runloop就是一直在线程上面画圆圈,一直在跑圈,除非切断否则一直在运行。网上说的比喻很好,直线就像昙花一现一样,圆就像os,一直运行直到你关机为止。

    在我们学习iOS生命周期里面都会存在销毁的过程,但是屏幕好像一直能接收各种指令,感觉很像runloop的功效,好像这些是和顶层UIKit无关,iOS架构最底层是Core OS,我分析应该是苹果封装好了,只是我们看不到源码而已。

 

为什么要使用runloop

     回到开始的疑问,为什么要使用runloop,一般情况下我们是没必要去启动线程的runloop,除非需要在一个单独的线程长久的检测某个事件,就像视频里面提到的类似微信的语音功能,见一个runloop专门负责监听说话的线程。看需求而定了。

 

CFRunLoopSource

           Source是RunLoop的数据源抽象类,类似iOS中protocol

           RunLoop定义两个Version的Source

           Source0:处理app内部事件,App自己负责管理(触发),如UIEvent,CFSocket

           Source1:由RunLoop和内核管理,Mach port驱动如CFMach、CFMessage

 

CFRunLoopObserver           

           向内部报告runloop当前状态的更改CAAnimation

 

RunLoopObserver 与 Autorelease Pool

         UIKit通过RunLoopObserver在RunLoop两次Sleep间对AutoreleasePool进行pop和push,将这次Loop中产生的Autorelease对象释放。(好像swift中没有关于释放的问题)

 

CFRunLoopMode

        runloop在同一时段只能且必须在一种特定Mode下Run更换Mode 时,需要暂停当前的loop,然后重启新的loop

     NSDefalutRunLoopMode      默认状态,空闲状态

     UITrackingRunLoopMode     滑动ScrollView

     UIInitializationRunLoopMode    私有,App启动时

     NSRunLoopCommonModes    默认包括上面第一和第二

 

UITrackingRunLoopMode 与 NSTimer

      默认情况下   NSTimer  被加入NSDefalutRunLoopMode,如果想NSTimer受到组件或者动画影响添加到NSRunLoopCommonModes(oc代码如下:)

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

 swift版代码:

     NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)

RunLoopMode切换      NSDefaultRunLoopMode->UITrackingRunLoopMode->NSDefalutRunLoopMode RunLoop的挂起与唤醒       制定用于唤醒的 mach_port端口       调用mach_msg监听唤醒端口,被唤醒前,系统内核将这个线程挂起,停留在mach_msg_trap              由另外一个线程(或另一个进程中的某个线程)向内核发送这个端口的msg后,trap状态被唤醒,RunLoop继续开始干活 AFNetWorking 中创建RunLoop       创建一个常驻服务线程的很好方法
               [[NSThread currentThread] setName:@"AFNetworking"];
               NSRunLoop *runloop = [NSRunLoop currentRunLoop];
               [runLoop addPort: [NSMachPort port] forMode:NSDefalutRunLoopMode]//一直活着
               [runLoop run];
swift版代码
      
var loop = NSRunLoop.currentRunLoop()
loop.addPort(NSMachPort(), forMode: NSDefaultRunLoopMode)   loop.run()

一个TableView延迟加载图片的新思路       

              [self.avatarImageView performSelector:@selector(serImage:) withObjetc:downloadedImage    afterDelay:0        inModes:@[NSDefaultRunLoopMode]] + (NSThread *)networkRequestThread {
              static NSThread *_networkRequestThread = nil;
              static dispatch_once_t oncePredicate;
             dispatch_once(&oncePredicate, ^{
            _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:)             object:nil];
          [_networkRequestThread start];
         });
             return _networkRequestThread;
 
这个代码无法转换成swift,可能是我没想到办法,大家谁找到了请评论,谢谢了。

     让Crash的App回光返照 只针对Signal Crash

         CFRunLoopRef runloop = CFRunLoopGetCurrent();
         NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runLoop));
        while(1){
                     for (NSString *mode in allModes){
                     CFRunLoopInMode((CFStringRef)mode,0.001,false);
                 }
          }
 
     runloop事件队列
    每次运行runloop,你线程的runloop对自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:
 
     1、通知观察者runloop已经启动
     2、通知观察者任何即将要开始的定时器
     3、通知观察者任何即将启动的非基于端口的源
     4、启动任何准备好的非基于端口的源
     5、如果基于端口的源准备好并处于等待状态,立即启动,并进入步骤9。
     6、通知观察者线程进入休眠
     7、将线程置于休眠直到任一下面的事件发生:
          某一事件到达基于端口的源
          定时器启动
          runloop设置的时间已经超时
          runloop被显示唤醒
     8、通知观察者线程将被唤醒
     9、处理未处理的事件
          如果用户定义的定时器启动,处理定时器事件并启动runloop。进入步骤2
          如果输入源启动,传递相应的消息
          如果runloop 被显式唤醒而且时间还没超时,重启run loop。进入步骤2
    10、通知观察者runloop结束
 
异步测试:
     
- (BOOL)runUntilBlock:(
                  BOOL(^)())block timeout:(NSTimeInterval)timeout{
                     __block Boolean fulfilled = NO;
                     void (^beforeWaiting) (
                     CFRunLoopObserverRef observer, CFRunLoopActivity activity) =          ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
                   fulfilled = block(); if (fulfilled) { CFRunLoopStop(CFRunLoopGetCurrent());
             }
    };
                   CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, beforeWaiting);
                   CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
                // Run! CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);      
                 CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);                       CFRelease(observer);
                 return fulfilled;

转载于:https://www.cnblogs.com/LiZeYuBlog/p/7425299.html

你可能感兴趣的文章
poj2255Tree Recovery【二叉树重构】
查看>>
tcpcopy 流量复制工具
查看>>
vue和react的区别
查看>>
第十一次作业
查看>>
负载均衡策略
查看>>
微信智能开放平台
查看>>
ArcGIS Engine 中的绘制与编辑
查看>>
Oracle--通配符、Escape转义字符、模糊查询语句
查看>>
c# 文件笔记
查看>>
第一页 - 工具的使用(webstorm)
查看>>
Linux 进程资源用量监控和按用户设置进程限制
查看>>
IE浏览器整页截屏程序(二)
查看>>
D3.js 之 d3-shap 简介(转)
查看>>
制作满天星空
查看>>
类和结构
查看>>
CSS3选择器(二)之属性选择器
查看>>
adidas crazylight 2018 performance analysis review
查看>>
typeset shell 用法
查看>>
python 之 循环语句
查看>>
心得25--JDK新特性9-泛型1-加深介绍
查看>>