RunLoop

2024-06-15  本文已影响0人  buding_
什么是RunLoop?
RunLoop的基本作用:
iOS中有2套API来访问和使用RunLoop
Foundtation: NSRunLoop
Core Foundation: CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC封装

NSRunLoop *r1 = [NSRunLoop currentRunLoop];
CFRunLoopRef r2 = CFRunLoopGetCurrent();


struct __CFRunLoop {
    pthread_t _pthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode; 
    CFMutableSetRef _modes;
};

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
};
Source0: 触摸事件、performSelector:onThread
Source1: 基于Port的线程间通信、系统事件捕捉
Timers:NSTimer、performSelector:withObject:afterDelay: 
Observers: 用于监听RunLoop的状态、UI刷新(BeforeWaiting)、Autorelease pool(BeforeWaiting)

CFRunLoopMode
常见的2种mode
kCFRunLoopDefaultMode: App的默认mode,通常主线程就是在这个Mode下运行
UITrackingRunLoopMode: 界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他mode影响

注意:NSRunLoopCommonModes并不是真的模式,它只是一个标记,设置为后,source会在_commonModes中存放的mode中运行;
_commonModes中默认存放的是kCFRunLoopDefaultMode和UITrackingRunLoopMode;
当source设置在NSRunLoopCommonModes模式后,那么该source会被存放到_commonModeItems中


typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),           //即将进入Loop
    kCFRunLoopBeforeTimers = (1UL << 1),    //即将处理Timer
    kCFRunLoopBeforeSources = (1UL << 2),   //即将处理Source
    kCFRunLoopBeforeWaiting = (1UL << 5),   //即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),    //刚才休眠中唤醒
    kCFRunLoopExit = (1UL << 7),            //即将退出Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

RunLoop休眠的原理:
通过mach_msg 从用户态进入内核态,从而可以使线程进入睡眠;   
RunLoop如何响应用户操作?

CFRunLoop的currentMode中的Source1捕捉到用户事件,包装成事件队列后传递给source0处理;
通知observers结束休眠,处理source;


RunLoop运行逻辑.png
应用范畴:

定时器、PerformSelector
GCD Async Main Queue
事件响应、手势识别、界面刷新

RunLoop在项目中的应用有

控制线程的生命周期

@interface ViewController ()
@property (nonatomic, strong) NSThread *thread;
@property (nonatomic, assign) BOOL isStopThread;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    __weak typeof(self) weakSelf = self;
    self.thread = [[NSThread alloc] initWithBlock:^{
        /*添加NSPort,让RunLoop立马销毁,
          因为当RunLoop中没有source0 source1 observer timer时,会立马销毁 */
        [[NSRunLoop currentRunLoop] addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
        //通过isStopThread标记位控制Loop
        while (weakSelf && !weakSelf.isStopThread) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        //NSRunLoop的run方法是无法停止的,它专门用于开启一个永不销毁的NSRunLoop
        //[[NSRunLoop currentRunLoop] run];
    }];
    [self.thread start];
}
- (void)test {
    NSLog(@" __ %s __ %@", __func__, [NSThread currentThread]);
}
- (void)stop {
    NSLog(@" __ %s __ %@", __func__, [NSThread currentThread]);
    //修改结束Loop的标记位
    _isStopThread = YES;
    //停止本次Loop
    CFRunLoopStop(CFRunLoopGetCurrent());
}
- (IBAction)onTestOnThread:(id)sender {
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}

- (IBAction)onStopThread:(id)sender {
    [self performSelector:@selector(stop) onThread:self.thread withObject:nil waitUntilDone:YES];
}
@end

解决NSTimer在滑动时停止工作的问题

static int count = 0;
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
  NSLog(@"%d",count++);
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

注意:NSRunLoopCommonModes并不是真的模式,它只是一个标记,设置为后,source会在_commonModes中存放的mode中运行;
_commonModes中默认存放的是kCFRunLoopDefaultMode和UITrackingRunLoopMode;
当source设置在NSRunLoopCommonModes模式后,那么该source会被存放到_commonModeItems中

上一篇下一篇

猜你喜欢

热点阅读