• iOS开发网络多线程之Runloop无限循环


    一. Runloop的基本知识

    1. 概念

        runloop是运动循环,不断跑圈,无限循环

       作用:

        保持程序的持续运行 (iOS程序一直活着的原因)

        处理App中的各种事件(eg:触摸事件/定时器事件/selector事件【选择器·performSelector···】)

        节省CPU资源,提高程序的性能(有事做事,没事休息)

    程序已启动,就开启了一个runloop无限循环,因此程序才能持续的运行

    1. #import <UIKit/UIKit.h>
    2. #import "AppDelegate.h"
    3. int main(int argc, char * argv[]) {
    4. @autoreleasepool {
    5. return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    6. }
    7. }


    2. Runloop对象

    1> 在iOS开发中有两套api来访问Runloop

    • Foundation框架【NSRunloop】(OC)

    • Core Foundation框架【CFRunloopRef】(C)

    2> NSRunLoop和CFRunLoopRef都代表着RunLoop对象,它们是等价的,可以互相转换

    3> NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)

      

    3. Runloop与线程的关系

    1> Runloop和线程的关系:一个Runloop对应着一条唯一的线程

        问题:如何让子线程不死

        回答:给这条子线程开启一个Runloop

    2> Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建

    3> Runloop的生命周期:在第一次获取时创建,在线程结束时销毁


    二. Runloop对象的创建

    1. Foundation框架【NSRunloop】创建Runloop对象

    1. // 获取当前线程下的Runloop, 懒加载的形式创建
    2. NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
    3. // 获取主线程下的Runloop
    4. NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];

    2. Core Foundation框架【CFRunloopRef】创建Runloop对象

    1. // 获取当前线程下的Runloop, 懒加载的形式创建
    2. CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
    3. // 获取主线程下的Runloop
    4. CFRunLoopRef runloop2 = CFRunLoopGetMain();


    3. Runloop运行原理图

    4. Runloop相关的类

    • CFRunloopRef

    • CFRunloopModeRefRunloop的运行模式】

    • CFRunloopSourceRefRunloop要处理的事件源】

    • CFRunloopTimerRefTimer事件】

    • CFRunloopObserverRefRunloop的观察者(监听者)】

        Runloop要想跑起来,它的内部必须要有一个mode,mode中必须有source/observer/time,至少要有其中的一个

     

    5. CFRunloopModeRefRunloop的运行模式】

        Runloop每次启动的时候只能指定一个运行模型,切换模式时必须退出当前的Runloop,再重新进入一个mode,是为了分割不同组的定时器互不影响

    • kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行

    • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

    • UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

    • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

    • kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode

    6. CFRunloopTimerRefTimer事件】

    1> NSTimer

    1. - (void)timer0
    2. {
    3. NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
    4. /*
    5. NSDefaultRunLoopMode -> kCFRunLoopDefaultMode 默认模式主线程中执行
    6. NSRunLoopCommonModes -> kCFRunLoopDefaultMode/UITrackingRunLoopMode
    7. UITrackingRunLoopMode -> 界面追踪,其他不工作
    8. */
    9. [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    10. }

    2. GCD定时器

    GCD定时器必须必须保存起来才能使用

    1. - (void)gcdTimer
    2. {
    3. NSLog(@"+++++++++");
    4. // 1.创建定时器
    5. dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    6. // 2.设置定时器
    7. /*
    8. 第一个参数:定时器
    9. 第二个参数:从哪个时间开始
    10. 第三个参数:间隔时间
    11. 第四个参数:精确度, 0代表无误差
    12. */
    13. dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    14. // 3.定时器触发事件
    15. dispatch_source_set_event_handler(timer, ^{
    16. NSLog(@"----------");
    17. });
    18. // 4.开启定时器
    19. dispatch_resume(timer);
    20. // GCD定时器创建是个局部变量需要保存才能执行
    21. self.timer = timer;
    22. }

    7. CFRunloopSourceRefRunloop要处理的事件源】

    1)以前的分法

    Port-Based Sources

    Custom Input Sources

    Cocoa Perform Selector Sources


    2)现在的分法

    Source0:非基于Port的 (用户触发的时间)

    Source1:基于Port的 (通过内核和其它线程相互发送消息)


    8. CFRunloopObserverRefRunloop的观察者(监听者)】

    1. - (void)observer
    2. {
    3. // 创建一个观察者
    4. /*
    5. 第一个参数:获取内存的方式
    6. 第二个参数:监听哪些状态
    7. 第三个参数:是否持续监听
    8. 第四个参数:0
    9. 第五个参数:block代码块
    10. kCFRunLoopEntry = (1UL << 0), 准备进入
    11. kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件
    12. kCFRunLoopBeforeSources = (1UL << 2), 即将处理source事件
    13. kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入休眠
    14. kCFRunLoopAfterWaiting = (1UL << 6), 唤醒
    15. kCFRunLoopExit = (1UL << 7), 退出
    16. kCFRunLoopAllActivities = 0x0FFFFFFFU 所有
    17. */
    18. CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    19. switch (activity) {
    20. case kCFRunLoopEntry:
    21. NSLog(@"准备进入");
    22. break;
    23. case kCFRunLoopBeforeTimers:
    24. NSLog(@"即将处理timer事件");
    25. break;
    26. case kCFRunLoopBeforeSources:
    27. NSLog(@"即将处理source事件");
    28. break;
    29. case kCFRunLoopBeforeWaiting:
    30. NSLog(@"即将进入休眠");
    31. break;
    32. case kCFRunLoopAfterWaiting:
    33. NSLog(@"唤醒");
    34. break;
    35. case kCFRunLoopExit:
    36. NSLog(@"退出");
    37. break;
    38. }
    39. });
    40. // 给runloop添加一个观察者
    41. CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    42. NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
    43. // 释放观察者
    44. CFRelease(observer);
    45. }


    9. Runloop运行逻辑




  • 相关阅读:
    Elasticsearch重要文章之四:监控每个节点(ThreadPool部分)
    [翻译]Elasticsearch重要文章之四:监控每个节点(jvm部分)
    IDFA
    Python中eval函数的作用
    Android开发重点难点:RelativeLayout(相对布局)详解
    布局相关
    Nginx配置proxy_pass
    Docker部署Elasticsearch集群
    extends Thread 与 implements Runnable 的区别
    玩转 Redis缓存 集群高可用
  • 原文地址:https://www.cnblogs.com/Xfsrn/p/5000322.html
Copyright © 2020-2023  润新知