RunLoop

作者: 万万万万万万一 | 来源:发表于2023-07-12 10:50 被阅读0次

    什么是RunLoop

    默认情况下,线程执行完任务后就会退出,不再执行任务。我们需要采用一种方式来让线程能够不断的处理任务,并且不退出,所有就有了RunLoop。

    顾名思义,RunLoop就是一直运行着的循环。
    RunLoop实际上是一个对象,这个对象在循环中用来处理程序运行过程中出现的各种事件(比如说touch事件、UI刷新事件、定时器事件、Selector事件),从而保持程序的持续运行。Runloop在没有事件处理时,会让线程进入休眠,只有在接收到事件时才会被唤醒,然后再做出相应的处理。

    一条线程对应一个RunLoop对象(内部实现好比字典[key:线程,value:runloop]),主线程的RunLoop对象由系统自动创建。

    线程与RunLoop的关系(通俗比方)
    线程比作汽车,RunLoop比做发动机,所以汽车才能一直跑下去。

    RunLoop Mode

    NSDefaultRunLoopMode
    app默认Mode,通常主线程就在该Mode下运行。

    NSTrackingRunLoopMode
    界面跟踪mode,用于scrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响。

    UIInitializationRunLoopMode
    在刚启动app时进入的第一个Mode,启动完成后就不再使用。

    GSEventReceiveRunLoopMode
    接收系统事件的内部Mode,通常用不到。

    NSRunLoopCommonModes
    这是一个占位Mode,不是一个真正的Mode,会同时处理默认模式和UI模式中的事件。

    RunLoop Source

    定时源,基于时间的触发器,基本上就是NSTimer
    NSTimer默认被加入RunLoop的default mode下

    source0:非基于Port的源,基本就是应用层事件,比如点击事件
    source1:基于Port的源,通过内核和其他线程通信,接收、分发系统事件

    我们点击app内的某个按钮,先触摸到屏幕(硬件),屏幕表面的事件会先封装成Event传递给source1(mach_port),source1唤醒runloop,让后将Event分发给source0处理。

    RunLoop Observer

    CFRunLoopObserverRef,监听RunLoop的状态改变
    即将进入Loop:1
    即将处理Timer:2
    即将进入source:4
    即将进入休眠:32
    即将从休眠中唤醒:64
    即将从Loop中退出:128
    监听全部状态改变

    RunLoop运行逻辑

    1.通知观察者即将进入RunLoop
    2.通知观察者即将处理Timer
    3.通知观察者即将处理source0
    4.处理source0
    5.如果有source1,跳至第9步
    6.通知观察者线程即将进入休眠状态
    7.线程进入休眠,等待直到以下任一事件发生唤醒线程
    某一事件到达基于端口的源
    定时器启动
    RunLoop设置的时间超时
    RunLoop被显式唤醒
    8.通知观察者线程即将被唤醒
    9.处理唤醒时收到的事件
    如果用户定义的定时器启动,处理定时器事件并重启RunLoop,跳回步骤2;
    如果输入源启动,传递相应消息;
    如果RunLoop被显式唤醒且时间未超时,重启RunLoop,跳回步骤2
    10.通知观察者,即将退出RunLoop

    RunLoop 可以做什么

    1.crash起死回生

    好在苹果自带的crash检测api,以及runloop可以让应用复活一次,不过第二次依旧会崩溃,但是还是一个很实用的小技巧。
    以下是IOS崩溃复活的类和使用方法

    #import "CrashHandler.h"
    
    //Appdelagte中注册crash记录上报
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      // Override point for customization after application launch.
      [CrashHandler sharedInstance];
      return YES;
    }
    
    //.h文件
    #import <Foundation/Foundation.h>
    
    @interface CrashHandler : NSObject
    {
      BOOL ignore;
    }
    
    + (instancetype)sharedInstance;
    
    @end
    
    //.m文件
    #import "CrashHandler.h"
    #import <UIKit/UIKit.h>
    #include <libkern/OSAtomic.h>
    #include <execinfo.h>
    
    NSString * const kSignalExceptionName = @"kSignalExceptionName";
    NSString * const kSignalKey = @"kSignalKey";
    NSString * const kCaughtExceptionStackInfoKey = @"kCaughtExceptionStackInfoKey";
    
    void HandleException(NSException *exception);
    void SignalHandler(int signal);
    
    @implementation CrashHandler
    
    static CrashHandler *instance = nil;
    
    + (instancetype)sharedInstance
    {
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
        instance = [[[self class] alloc] init];
      });
      return instance;
    }
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
      });
      return instance;
    }
    
    - (instancetype)init
    {
      self = [super init];
      if (self) {
        [self setCatchExceptionHandler];
      }
      return self;
    }
    
    - (void)setCatchExceptionHandler
    {
      // 1.捕获一些异常导致的崩溃
      NSSetUncaughtExceptionHandler(&HandleException);
       
      // 2.捕获非异常情况,通过signal传递出来的崩溃
      signal(SIGABRT, SignalHandler);
      signal(SIGILL, SignalHandler);
      signal(SIGSEGV, SignalHandler);
      signal(SIGFPE, SignalHandler);
      signal(SIGBUS, SignalHandler);
      signal(SIGPIPE, SignalHandler);
    }
    
    + (NSArray *)backtrace
    {
      void* callstack[128];
      int frames = backtrace(callstack, 128);
      char **strs = backtrace_symbols(callstack, frames);
       
      NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
      for (int i = 0; i < frames; i++) {
        [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
      }
      free(strs);
       
      return backtrace;
    }
    
    - (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex
    {
      if (anIndex == 0) {
        ignore = YES;
      } else if (anIndex == 1) {
        NSLog(@"起死回生");
      }
    }
    
    - (void)handleException:(NSException *)exception
    {
      NSString *message = [NSString stringWithFormat:@"崩溃原因如下:\n%@\n%@",
                 [exception reason],
                 [[exception userInfo] objectForKey:kCaughtExceptionStackInfoKey]];
      NSLog(@"%@",message);
       
      UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"程序崩溃了"
                              message:@"如果你能让程序起死回生,那你的决定是?"
                              delegate:self
                         cancelButtonTitle:@"崩就蹦吧"
                         otherButtonTitles:@"起死回生", nil];
      [alert show];
       
      CFRunLoopRef runLoop = CFRunLoopGetCurrent();
      CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
       
      while (!ignore) {
        for (NSString *mode in (__bridge NSArray *)allModes) {
          CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
        }
      }
       
      CFRelease(allModes);
       
      NSSetUncaughtExceptionHandler(NULL);
      signal(SIGABRT, SIG_DFL);
      signal(SIGILL, SIG_DFL);
      signal(SIGSEGV, SIG_DFL);
      signal(SIGFPE, SIG_DFL);
      signal(SIGBUS, SIG_DFL);
      signal(SIGPIPE, SIG_DFL);
       
      if ([[exception name] isEqual:kSignalExceptionName]) {
        kill(getpid(), [[[exception userInfo] objectForKey:kSignalKey] intValue]);
      } else {
        [exception raise];
      }
    }
    
    @end
    
    void HandleException(NSException *exception)
    {
      // 获取异常的堆栈信息
      NSArray *callStack = [exception callStackSymbols];
      NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
      [userInfo setObject:callStack forKey:kCaughtExceptionStackInfoKey];
       
      CrashHandler *crashObject = [CrashHandler sharedInstance];
      NSException *customException = [NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo];
      [crashObject performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES];
    }
    
    void SignalHandler(int signal)
    {
      // 这种情况的崩溃信息,就另某他法来捕获吧
      NSArray *callStack = [CrashHandler backtrace];
      NSLog(@"信号捕获崩溃,堆栈信息:%@",callStack);
       
      CrashHandler *crashObject = [CrashHandler sharedInstance];
      NSException *customException = [NSException exceptionWithName:kSignalExceptionName
                                  reason:[NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.", nil),signal]
                                 userInfo:@{kSignalKey:[NSNumber numberWithInt:signal]}];
       
      [crashObject performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES];
    }
    
    
    
    

    2.线程保活

    如果经常会在子线程中做一些耗时操作(比如下载文件、后台播放音乐等),最好能让这条线程常驻内存

    为当前线程添加一个RunLoop
    向该RunLoop中添加一个Port/Source等维持RunLoop的事件循环
    启动该RunLoop

    3.app的卡顿监控,保活子线程 添加定时器 监控主线程RunLoop的状态 (封装单例工具类)

    相关文章

      网友评论

          本文标题:RunLoop

          本文链接:https://www.haomeiwen.com/subject/wetpudtx.html