zoukankan      html  css  js  c++  java
  • iOS runloop

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

    初识runloop

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

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

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


    为什么要使用runloop

    @sunnnyxx 在视频介绍了四个作用:

    • 使程序一直运行接受用户输入
    • 决定程序在何时应该处理哪些Event
    • 调用解耦(对于编程经验为0的完全没搞懂这个意思,解释为Message Queue)
    • 节省CPU时间

       这段视频我觉得不太适合小白去看,因为好多概念还没有融会贯通,也没有理解透彻。但是既然看了,就得总结一下,至少产生一个树突先,留下一个问号,未来把问号变成叹号。

       回到开始的疑问,为什么要使用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事件队列
    每次运行run loop,你线程的run loop对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:

    1. 通知观察者run loop已经启动
    2. 通知观察者任何即将要开始的定时器
    3. 通知观察者任何即将启动的非基于端口的源
    4. 启动任何准备好的非基于端口的源
    5. 如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9。
    6. 通知观察者线程进入休眠
    7. 将线程置于休眠直到任一下面的事件发生:
      • 某一事件到达基于端口的源
      • 定时器启动
      • Run loop设置的时间已经超时
      • run loop被显式唤醒
    8. 通知观察者线程将被唤醒。
    9. 处理未处理的事件
      • 如果用户定义的定时器启动,处理定时器事件并重启run loop。进入步骤2
      • 如果输入源启动,传递相应的消息
      • 如果run loop被显式唤醒而且时间还没超时,重启run loop。进入步骤2
    10. 通知观察者run loop结束。

    异步测试

    - (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;

    }



    文/小白和小黑(简书作者)
    原文链接:http://www.jianshu.com/p/613916eea37f
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
  • 相关阅读:
    hdu 2647 Reward
    hdu 2094 产生冠军
    hdu 3342 Legal or Not
    hdu 1285 确定比赛名次
    hdu 3006 The Number of set
    hdu 1429 胜利大逃亡(续)
    UVA 146 ID Codes
    UVA 131 The Psychic Poker Player
    洛谷 P2491消防 解题报告
    洛谷 P2587 [ZJOI2008]泡泡堂 解题报告
  • 原文地址:https://www.cnblogs.com/NSong/p/5481078.html
Copyright © 2011-2022 走看看