RunLoop OC

RunLoop

Posted on 2021-10-17,3 min read

RunLoop 相关


简介

基本作用

  1. 保持程序的持续运行
  2. 处理APP中的各种事件(比如触摸事件、定时器事件等)
  3. 节约CPU性能,提高程序性能;该做事时做事,该休息时休息
  4. .....

RunLoop对象

  • iOS中有2套API来访问和使用RunLoop

    • Foundation:NSRunLoop
    • Core Foundation:CFRunLoopRef
  • 获取当前RunLoop

    NSRunLoop *runloop = [NSRunLoop currentRunLoop]; // OC
    CFRunLoopRef runloop2 = CFRunLoopGetCurrent(); // C
    
  • NSRunLoop和CFRunLoopRef都代表着RunLoop对象,NSRunLoop是基于CFRunLoopRef的一层OC包装;
    CFRunLoopRef是开源的 :https://opensource.apple.com/tarballs/CF/

RunLoop与线程

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
  • 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
  • RunLoop会在线程结束时销毁
  • 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

获取RunLoop对象

//Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

//Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象

RunLoop相关的类

RunLoop相关的类

CFRunLoopModeRef

  • CFRunLoopModeRef代表RunLoop的运行模式
  • 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
  • RunLoop启动时只能选择其中一个Mode,作为currentMode
  • 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入;不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
  • 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
  • 常见的2种Mode
    1. kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
    2. UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

CFRunLoopObserverRef

CFRunLoopObserverRef

添加Observer监听RunLoop的所有状态

RunLoop的所有状态

RunLoop的运行逻辑

RunLoop的运行逻辑

线程保活

DNPermenantTheread.h

@interface DNPermenantTheread : NSObject

/// 开启线程
- (void)run;

/// 执行任务
/// @param task 任务
- (void)executeTask:(void(^)(void))task;

/// 关闭线程
- (void)stop;

@end

DNPermenantTheread.m

#import "DNPermenantTheread.h"
#import <objc/runtime.h>

@interface DNPermenantTheread()

@property (nonatomic, assign, getter=isStopped) BOOL stopped;
@property (nonatomic, strong) NSThread *innerThread;

@end

@implementation DNPermenantTheread

- (instancetype)init{
    if (self = [super init]) {
        self.stopped = NO;
        
        __weak typeof(self) weakSelf = self;
        
        self.innerThread = [[NSThread alloc] initWithBlock:^{
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
            
            while (weakSelf && !weakSelf.isStopped) {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
            }
        }];
    }
    return self;
}

#pragma mark - public methods
- (void)run{
    if (!_innerThread) return;
    [self.innerThread start];
}

- (void)executeTask:(void(^)(void))task{
    if (!_innerThread || !task) return;
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

- (void)stop{
    if (!_innerThread) return;
    [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}


#pragma mark - private methods
- (void)__stop{
    self.stopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
}

- (void)__executeTask:(void(^)(void))task{
    task();
}

@end

下一篇: Runtime-API→

loading...