iOS开发网络多线程之Runloop无限循环
一. Runloop的基本知识
1. 概念
runloop是运动循环,不断跑圈,无限循环
作用:
保持程序的持续运行 (iOS程序一直活着的原因)
处理App中的各种事件(eg:触摸事件/定时器事件/selector事件【选择器·performSelector···】)
节省CPU资源,提高程序的性能(有事做事,没事休息)
程序已启动,就开启了一个runloop无限循环,因此程序才能持续的运行
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
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对象
// 获取当前线程下的Runloop, 懒加载的形式创建
NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
// 获取主线程下的Runloop
NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];
2. Core Foundation框架【CFRunloopRef】创建Runloop对象
// 获取当前线程下的Runloop, 懒加载的形式创建
CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
// 获取主线程下的Runloop
CFRunLoopRef runloop2 = CFRunLoopGetMain();
3. Runloop运行原理图
4. Runloop相关的类
CFRunloopRef
CFRunloopModeRef【Runloop的运行模式】
CFRunloopSourceRef【Runloop要处理的事件源】
CFRunloopTimerRef【Timer事件】
CFRunloopObserverRef【Runloop的观察者(监听者)】
Runloop要想跑起来,它的内部必须要有一个mode,mode中必须有source/observer/time,至少要有其中的一个
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
- (void)timer0
{
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
/*
NSDefaultRunLoopMode -> kCFRunLoopDefaultMode 默认模式主线程中执行
NSRunLoopCommonModes -> kCFRunLoopDefaultMode/UITrackingRunLoopMode
UITrackingRunLoopMode -> 界面追踪,其他不工作
*/
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
}
2. GCD定时器
GCD定时器必须必须保存起来才能使用
- (void)gcdTimer
{
NSLog(@"+++++++++");
// 1.创建定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
// 2.设置定时器
/*
第一个参数:定时器
第二个参数:从哪个时间开始
第三个参数:间隔时间
第四个参数:精确度, 0代表无误差
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
// 3.定时器触发事件
dispatch_source_set_event_handler(timer, ^{
NSLog(@"----------");
});
// 4.开启定时器
dispatch_resume(timer);
// GCD定时器创建是个局部变量需要保存才能执行
self.timer = timer;
}
7. CFRunloopSourceRef【Runloop要处理的事件源】
(1)以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources
(2)现在的分法
Source0:非基于Port的 (用户触发的时间)
Source1:基于Port的 (通过内核和其它线程相互发送消息)
8. CFRunloopObserverRef【Runloop的观察者(监听者)】
- (void)observer
{
// 创建一个观察者
/*
第一个参数:获取内存的方式
第二个参数:监听哪些状态
第三个参数:是否持续监听
第四个参数:0
第五个参数:block代码块
kCFRunLoopEntry = (1UL << 0), 准备进入
kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件
kCFRunLoopBeforeSources = (1UL << 2), 即将处理source事件
kCFRunLoopBeforeWaiting = (1UL << 5), 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), 唤醒
kCFRunLoopExit = (1UL << 7), 退出
kCFRunLoopAllActivities = 0x0FFFFFFFU 所有
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"准备进入");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timer事件");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理source事件");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"唤醒");
break;
case kCFRunLoopExit:
NSLog(@"退出");
break;
}
});
// 给runloop添加一个观察者
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(show) userInfo:nil repeats:YES];
// 释放观察者
CFRelease(observer);
}
9. Runloop运行逻辑