iOS runloop

作者: 小白和小黑 | 来源:发表于2015-04-04 04:02 被阅读43663次

    写这篇文章开始之前,我都不知道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被显式唤醒
    1. 通知观察者线程将被唤醒。
    2. 处理未处理的事件
    • 如果用户定义的定时器启动,处理定时器事件并重启run loop。进入步骤2
    • 如果输入源启动,传递相应的消息
    • 如果run loop被显式唤醒而且时间还没超时,重启run loop。进入步骤2
    1. 通知观察者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;
    

    }


    总结一下

    基本上对于RunLoop,我只能说我只有简单的模糊印象,但是不理解,也不太清楚在现实如何使用,因为实践才能知道如何做,真的在脑里形成了树突,希望在未来搞定,研究透彻。看这些代码时候我发现我的基础知识还需要巩固。感谢不相识的孙源。
    声明一点:不要只去收藏,去看看代码有没有问题,知识有没有解释错误的,交流学的更快。

    相关文章

      网友评论

      • LD_左岸:请教下大神:
        我现在有一个TabbarController 里面放了两个UIViewController
        分别为OneVc 和 TwoVc

        现在 TwoVc的- (void)viewDidLoad {
        [super viewDidLoad];
        self.tabBarController.selectedIndex = 0;
        };
        这么写 发现 并不给切换

        但是我这么写
        - (void)viewDidLoad {
        [super viewDidLoad];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.tabBarController.selectedIndex = 0;
        });


        }

        可以切换

        这个和Runloop有关吗???请大神点拨一二
        LD_左岸:@明明明Y 延时写的0秒啊
        我在viewWillApplear 里用这个延时0秒就能切换
        不延迟0秒就不能切换
        明明明Y:而且 tabbar 对已经加载的 View ,只加载一次就会持有, viewdidload 只走一次的, 那个 GCD 的方法产生了延时是 twovc 加载完毕
        明明明Y:如果没错的话,你这个和 runloop 关系不大, 和视图的声明周期有关的,self.tabBarController.selectedIndex = 0;放到 viewdidapear里面.
        另外这篇文章引用别人的不指出来不厚道
      • 音乐壁纸:转载了
      • 2d3df2091ad8:这个在tableview上使用会节省很多CPU时间,不会造成卡顿,不流畅的现象,需要慢慢理解深入研究才行。www.5200pt.com
      • 火郎君:RunLoop在同一时段只能且必须在一种特定Mode下Run
        更换Mode时, 需要暂停当前的Loop,然后重启新的Loop? 重启新的loop是不是不对啊
      • 生气龙:学习了
      • qBryant:学习 mark~!
      • Stark_Dylan:sunnnyxx ppt有木有。。。
      • 6a8c7e0e9557:一个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;
        }

        文/小白和小黑(简书作者)
        原文链接:http://www.jianshu.com/p/613916eea37f
        著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

        求解释 怎么用的
      • 6e2619899b1f: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(target:self, selector:selector(networkRequestThreadEntryPoint:), object:nil)

        _networkRequestThread.start()
        })

        var _networkRequestThread:return!

        }


        你要的斯威夫特代码
        sunnyxg0812:downloadedImage:是下载完成之后的一张图片或者是本地图片也行 UIImage类型的,
        selector(setImage:) 是来设置图片的set方法
        这面包有毒:虽然这种方法可以延迟加载图片,但是tabView滚动完成再切换到defaultmodel中是一次性加载、渲染图片,会导致几秒的空白,并不完美,最好是用数组保存刷新image的代码块,tabView滚动完,回掉block,每循环一次runloop刷新一张image
      • 陆壹吱吱:很好,多谢
      • 3211b3886c47:直线是没有终点的,线段才有。
        f582eef5ecf6:@idhong 这扣的,牛鼻啊:joy:
        liangdahong:@慕容曦 :joy:
      • 90e35846f83b:能给出sunnnyxx的视频地址么?谢谢
        半尺尘:@胖子洋 http://v.youku.com/v_show/id_XODgxODkzODI0.html 或者搜索:iOS线下分享《RunLoop》by 孙源@sunnyxx
      • 小白和小黑:@a4975e0f02b0 好的,已经更改。
      • a4975e0f02b0:是iOS而不是IOS。
      • 狍子君:不错

      本文标题:iOS runloop

      本文链接:https://www.haomeiwen.com/subject/nllaxttx.html