浅谈系列-RunLoop的运行逻辑是怎样的

导语:我们都知道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

复制代码
  • 监控性能卡顿

  • 优化性能

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享