导语:我们都知道RunLoop是保持iOS程序可以一直运行的关键,那么RunLoop到底是什么,运行逻辑是怎么样的呢?今天我们探讨一下。
RunLoop定义
-
RunLoop顾名思义就是运行循环,通过循环运行做一些事情去保持程序一直运行。
-
作用:事件响应,手势识别,UI刷新,定时器(timer),Autoreleasepool,PerformSelector,GCD Async Main Queue,网络请求 等都使用到RunLoop。
每条线程都有与之相对应的唯一RunLoop对象
主线程的RunLoop默认是开启的(保持程序运行),子线程的RunLoop默认是不开启的(线程第一次获取RunLoop的时候开启)
RunLoop会随着线程结束而销毁
RunLoop保存在一个全局字典里面,使用线程作为Key取出对应RunLoop
Foundation的RunLoop
- 创建RunLoop
NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
复制代码
- mode
NSRunLoopMode model;
NSDefaultRunLoopMode; //默认模式
UITrackingRunLoopMode; //scrollView滚动使用此模式
NSRunLoopCommonModes;//兼顾以上两种模式
复制代码
Core Foundation的RunLoop
- 创建RunLoop
CFRunLoopRef mainRL = CFRunLoopGetMain();;
CFRunLoopRef currentRL = CFRunLoopGetCurrent();
复制代码
- mode
CFRunLoopMode mode;
kCFRunLoopDefaultMode;
kCFRunLoopCommonModes;
复制代码
NSRunLoop是对CFRunLoopRef的一层OC封装。
- 监听RunLoop,只有c语言函数接口
监听方法一
//1.创建观察者
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, observerRunLoopActivities, NULL);
//2.添加观察者
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
//3.释放观察者
CFRelease(observer);
复制代码
//监听函数
void observerRunLoopActivities(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
switch (activity) {
case kCFRunLoopEntry://即将进入RunLoop
NSLog(@"kCFRunLoopEntry");
break;
case kCFRunLoopBeforeTimers://即将处理timer
NSLog(@"kCFRunLoopBeforeTimers");
break;
case kCFRunLoopBeforeSources://即将处理source
NSLog(@"kCFRunLoopBeforeSources");
break;
case kCFRunLoopBeforeWaiting://即将进入休眠
NSLog(@"kCFRunLoopBeforeWaiting");
break;
case kCFRunLoopAfterWaiting://刚从休眠中唤醒
NSLog(@"kCFRunLoopAfterWaiting");
break;
case kCFRunLoopExit://即将退出RunLoop
NSLog(@"kCFRunLoopExit");
break;
// case kCFRunLoopAllActivities://所有活动
// NSLog(@"kCFRunLoopAllActivities");
// break;
default:
break;
}
}
复制代码
监听方法二(通过block监听)
//1.创建观察者
CFRunLoopObserverRef observer2 = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry://即将进入RunLoop
NSLog(@"kCFRunLoopEntry");
break;
//其他状态。。。省略
case kCFRunLoopExit://即将退出RunLoop
NSLog(@"kCFRunLoopExit");
break;
default:
break;
}
});
//2.添加观察者
CFRunLoopAddObserver(CFRunLoopGetMain(), observer2, kCFRunLoopCommonModes);
//3.释放观察者
CFRelease(observer2);
复制代码
Core Foundation中关于RunLoop的5个类
CFRunLoopRef | RunLoop |
CFRunLoopMode | RunLoop运行模式 |
CFRunLoopSourceRef | source0,source1 |
CFRunLoopTimerRef | 定时器timer |
CFRunLoopObserverRef | 观察者-监听RunLoop |
- CFRunLoopMode(NSRunLoopMode)
一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
RunLoop启动时只能选择其中一个Mode,作为currentMode
如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
(不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响)如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
- CFRunLoopObserverRef
观察者可以监听RunLoop的活动状态,如下
CFRunLoopActivity 活动状态
kCFRunLoopEntry //即将进入RunLoop
kCFRunLoopBeforeTimers //即将处理timer
kCFRunLoopBeforeSources //即将处理source
kCFRunLoopBeforeWaiting //即将进入休眠
kCFRunLoopAfterWaiting //刚从休眠中唤醒
kCFRunLoopExit //即将退出RunLoop
kCFRunLoopAllActivities //所有活动
复制代码
RunLoop运行逻辑
RunLoop运行逻辑 | |
---|---|
01、通知Observers:进入Loop | |
02、通知Observers:即将处理Timers | |
03、通知Observers:即将处理Sources | |
04、处理Blocks | |
05、处理Source0(可能会再次处理Blocks) | |
06、如果存在Source1,就跳转到第8步 | —–>跳到08 |
07、通知Observers:开始休眠(等待消息唤醒) | |
08、通知Observers:结束休眠(被某个消息唤醒) | <—–从06调到 |
—01> 处理Timer | |
—02> 处理GCD Async To Main Queue | |
—03> 处理Source1 | |
09、处理Blocks | |
10、根据前面的执行结果,决定如何操作 | |
—01> 回到第02步 | |
—02> 退出Loop | |
11、通知Observers:退出Loop |
- source0 :
触摸事件处理
performSelector:onThread:
复制代码
- source1 :
基于Port的线程间通信
系统事件捕捉
复制代码
例如, source1捕捉点击事件,source0处理点击事件
- Timers
NSTimer
performSelector:withObject:afterDelay:
复制代码
- Observers
用于监听RunLoop的状态
UI刷新(BeforeWaiting休眠之前)
Autoreleasepool(BeforeWaiting休眠之前)
复制代码
RunLoop应用
- 解决NSTimer在滑动时停止问题
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
复制代码
- 线程保活
封装了一个线程保活工具类
FRPermenantThread.h
#import <Foundation/Foundation.h>
typedef void (^myTask) (void);
@interface FRPermenantThread : NSObject
/**
在子线程中执行任务
*/
- (void)executeTask:(myTask)task;
/**
停止
*/
- (void)stop;
@end
复制代码
FRPermenantThread.m
#import "FRPermenantThread.h"
//----------
//使用自定义线程,调试是不是可以正常释放FRThread
//@interface FRThread : NSThread
//@end
//
//@implementation FRThread
//- (void)dealloc
//{
// NSLog(@"%s",__func__);
//}
//@end
//-----------
@interface FRPermenantThread ()
@property (nonatomic, strong) NSThread *thread;
@property (nonatomic, assign, getter=isStoped) BOOL stoped;
@end
@implementation FRPermenantThread
#pragma mark - 公开接口
- (instancetype)init
{
self = [super init];
if (self) {
self.stoped = NO;
__weak typeof(self) weakSelf = self;
self.thread = [[NSThread alloc] initWithBlock:^{
// NSLog(@"子线程开始执行任务---");
//添加port timer source 唤醒runloop
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode: NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop] run];//调用run是无法停止的
while (weakSelf && !weakSelf.isStoped) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
// NSLog(@"子线程任务流程结束---");
}];
//开启子线程
[self.thread start];
}
return self;
}
//在子线程执行任务
- (void)executeTask:(myTask)task{
if (!self.thread && !task) return;
[self performSelector:@selector(__doSomeThing:) onThread:self.thread withObject:task waitUntilDone:NO];
}
- (void)stop{
if (!self.thread) return;
[self performSelector:@selector(__stop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
#pragma mark - 私有接口
//需要停止RunLoopStop也是需要在对应的子线中操作,要不然CFRunLoopGetCurrent可能获取的是其他的线程
- (void)__stop{
self.stoped = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.thread = nil;
}
- (void)__doSomeThing:(myTask)tast{
tast();
}
- (void)dealloc
{
[self stop];
// NSLog(@"%s",__func__);
}
@end
复制代码
-
监控性能卡顿
-
优化性能