转载

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运行原理图

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

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

4. Runloop相关的类

  • CFRunloopRef

  • CFRunloopModeRef Runloop 的运行模式】

  • CFRunloopSourceRef Runloop 要处理的事件源】

  • CFRunloopTimerRef Timer 事件】

  • CFRunloopObserverRef Runloop 的观察者(监听者)】

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

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

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

5. CFRunloopModeRef Runloop 的运行模式】

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

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

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

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

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

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

6. CFRunloopTimerRef Timer 事件】

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. CFRunloopSourceRef Runloop 要处理的事件源】

1 )以前的分法

Port-Based Sources

Custom Input Sources

Cocoa Perform Selector Sources

2 )现在的分法

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

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

8. CFRunloopObserverRef Runloop 的观察者(监听者)】

  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运行逻辑

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

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

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

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

正文到此结束
Loading...