RunLoop相关知识

如何实现一个多线程

  • runloop是通过内部维护的事件循环来对事件/消息进行管理的一个对象

    • 事件循环
      • 没有消息需要处理时进行休眠防止资源占用,即使线程从用户态转化为内核态
      • 有消息需要处理时立刻被唤醒,即使线程从内核态转化为用户态
  • 在main函数中会调用一个UIApplication函数,该函数中会启动一个主线程的runloop来维持主线程状态的切换

CFRunloop结构

  • CFRunLoop
struct __CFRunLoop {
    pthread_t _pthread; //runloop和线程是一一对应的关系,runloop可以脱离线程独自存在
    CFMutableSetRef _commonModes;	 //NSMutableSet<NSString *> 字符串类型的集合
    CFMutableSetRef _commonModeItems;	//集合,其中包含Observer/timer/source
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;	//NSMutableSet<CFRunLoopMode *>多个mode的集合
};
  • CFRunLoopMode
struct __CFRunLoopMode {
   CFStringRef _name;	//字符串描述mode类型,当添加到mode中时通过字符串对应的名称进行添加 
   CFMutableSetRef _sources0;		//MutableSet  无序
   CFMutableSetRef _sources1;		//MutableSet
   CFMutableArrayRef _observers;	//MutableArray  有序
   CFMutableArrayRef _timers;		//MutableArray
}
  • sources0
    • 需要手动唤醒线程,
  • sources1
    • 有自动唤醒线程的能力
  • CFRunLoopTimer
    • 基于事件的定时器,与NSTimer基于toll-free bridged互相转换
  • CGRunLoopObserver
    • 观察点
      • kCFRunLoopEntry 将要进入
      • kCFRunLoopBeforeTimers 将要对timer事件进行处理
      • kCFRunLoopBeforeSources 将要处理sources事件
      • kCFRunLoopBeforeWaiting 即将进入睡眠
      • kCFRunLoopAfterWaiting 被唤醒
      • kCFRunLoopExit 退出
      • kCFRunLoopAllActivities 所有的状态

//监测runloop的状态,

-(void)observer
{
    //1.创建监听者
    /*
     第一个参数:怎么分配存储空间
     第二个参数:要监听的状态 kCFRunLoopAllActivities 所有的状态
     第三个参数:时候持续监听
     第四个参数:优先级 总是传0
     第五个参数:当状态改变时候的回调
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        
        /*
         kCFRunLoopEntry = (1UL << 0),        即将进入runloop
         kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件
         kCFRunLoopBeforeSources = (1UL << 2),即将处理source事件
         kCFRunLoopBeforeWaiting = (1UL << 5),即将进入睡眠
         kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒
         kCFRunLoopExit = (1UL << 7),         runloop退出
         kCFRunLoopAllActivities = 0x0FFFFFFFU
         */
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"即将进入runloop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"即将处理timer事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"即将处理source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"即将进入睡眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"被唤醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"runloop退出");
                break;
                
            default:
                break;
        }
    });
    
    /*
     第一个参数:要监听哪个runloop
     第二个参数:观察者
     第三个参数:运行模式
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer, kCFRunLoopDefaultMode);
    
    //NSDefaultRunLoopMode == kCFRunLoopDefaultMode
    //NSRunLoopCommonModes == kCFRunLoopCommonModes
}

  • RunLoop有多个mode的原因
    • 不同的mode对应不同的状态,便于进行事件的屏蔽,起到事件隔离的作用

FPS监测实现

  • 方法一:通过监听RunLoop循环实现监测
#import "SMLagMonitor.h"

@interface SMLagMonitor() {
    int timeoutCount;
    CFRunLoopObserverRef runLoopObserver;
    @public
    dispatch_semaphore_t dispatchSemaphore;
    CFRunLoopActivity runLoopActivity;
}
@property (nonatomic, strong) NSTimer *cpuMonitorTimer;
@end

@implementation SMLagMonitor

#pragma mark - Interface
+ (instancetype)shareInstance {
    static id instance = nil;
    static dispatch_once_t dispatchOnce;
    dispatch_once(&dispatchOnce, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (void)beginMonitor {
    //判断observer是否已经存在,如果存在则处于启动状态直接返回
    if (runLoopObserver) {
        return;
    }
    dispatchSemaphore = dispatch_semaphore_create(0); //Dispatch Semaphore保证同步
    //创建一个观察者
    CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
    runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                              kCFRunLoopAllActivities,
                                              YES,
                                              0,
                                              &runLoopObserverCallBack,
                                              &context);
    //将观察者添加到主线程runloop的common模式下的观察中
    CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
    
    //创建子线程监控
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"开启子线程监测");
        
        //子线程开启一个持续的loop用来进行监控
        while (YES) {
            // 通过while和dispatch_semaphore_wait实现周期性的循环扫描
            long semaphoreWait = dispatch_semaphore_wait(dispatchSemaphore, dispatch_time(DISPATCH_TIME_NOW, 20*NSEC_PER_MSEC));
            if (semaphoreWait != 0) {
                // 判断observer是否存在,如果不存在则处于停止监控状态,重置数据后返回
                if (!runLoopObserver) {
                    timeoutCount = 0;
                    dispatchSemaphore = 0;
                    runLoopActivity = 0;
                    return;
                }
                //两个runloop的状态,BeforeSources和AfterWaiting这两个状态区间时间能够检测到是否卡顿
                if (runLoopActivity == kCFRunLoopBeforeSources || runLoopActivity == kCFRunLoopAfterWaiting) {
                    //出现三次出结果
//                    if (++timeoutCount < 3) {
//                        continue;
//                    }
                    NSLog(@"monitor trigger");
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                        // 出现卡顿状态可进行后面的u业务处理
                    });
                } //end activity
            }// end semaphore wait
            timeoutCount = 0;
        }// end while
    });
    
}

// 停止监控的方法
- (void)endMonitor {
    [self.cpuMonitorTimer invalidate];
    if (!runLoopObserver) {
        return;
    }
    CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes);
    CFRelease(runLoopObserver);
    runLoopObserver = NULL;
}

#pragma mark - Private
static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    SMLagMonitor *lagMonitor = (__bridge SMLagMonitor*)info;
    lagMonitor->runLoopActivity = activity;
    
    dispatch_semaphore_t semaphore = lagMonitor->dispatchSemaphore;
    dispatch_semaphore_signal(semaphore);
}

@end

  • 方法二:通过CADisplayLink进行监测,该方法通过CADisplayLink的频率和界面刷新频率的一致性来监测界面卡顿
    self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
    [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    
- (void)tick:(CADisplayLink *)link {
    if (_lastTime == 0) {
        _lastTime = link.timestamp;
        return;
    }
    _count++;
    NSTimeInterval delta = link.timestamp - _lastTime;
    if (delta < 1) return;
    _lastTime = link.timestamp;
    float fps = _count / delta;
    NSLog(@"帧率%f", fps);
    _count = 0;
}

原文地址:https://www.cnblogs.com/GoodmorningMr/p/11981000.html