面试

iOS - RunLoop

2021-04-19  本文已影响0人  lieon

RunLoop

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

RunLoop对象

 [NSRunLoop currentRunLoop]; // 获取当前线程的RunLoop对象
 [NSRunLoop mainRunLoop]; // 获取主线程的RunLoop对象
        
 CFRunLoopGetCurrent(); // 获取当前线程的RunLoop对象
 CFRunLoopGetMain(); // 获取主线程的RunLoop对象

RunLoop与线程

证明子线程中默认没有runloop对象


- (void) start
{
  pthread_attr_t    attr;

  if (_active == YES)
    {
      [NSException raise: NSInternalInconsistencyException
                  format: @"[%@-%@] called on active thread",
        NSStringFromClass([self class]),
        NSStringFromSelector(_cmd)];
    }
  if (_cancelled == YES)
    {
      [NSException raise: NSInternalInconsistencyException
                  format: @"[%@-%@] called on cancelled thread",
        NSStringFromClass([self class]),
        NSStringFromSelector(_cmd)];
    }
  if (_finished == YES)
    {
      [NSException raise: NSInternalInconsistencyException
                  format: @"[%@-%@] called on finished thread",
        NSStringFromClass([self class]),
        NSStringFromSelector(_cmd)];
    }

  /* Make sure the notification is posted BEFORE the new thread starts.
   */
  gnustep_base_thread_callback();

  /* The thread must persist until it finishes executing.
   */
  RETAIN(self);

  /* Mark the thread as active while it's running.
   */
  _active = YES;

  errno = 0;
  pthread_attr_init(&attr);
  /* Create this thread detached, because we never use the return state from
   * threads.
   */
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  /* Set the stack size when the thread is created.  Unlike the old setrlimit
   * code, this actually works.
   */
  if (_stackSize > 0)
    {
      pthread_attr_setstacksize(&attr, _stackSize);
    }
  if (pthread_create(&pthreadID, &attr, nsthreadLauncher, self))
    {
      DESTROY(self);
      [NSException raise: NSInternalInconsistencyException
                  format: @"Unable to detach thread (last error %@)",
                  [NSError _last]];
    }
}


static void *
nsthreadLauncher(void *thread)
{
  NSThread *t = (NSThread*)thread;

  setThreadForCurrentThread(t);

  /*
   * Let observers know a new thread is starting.
   */
  if (nc == nil)
    {
      nc = RETAIN([NSNotificationCenter defaultCenter]);
    }
  [nc postNotificationName: NSThreadDidStartNotification
            object: t
          userInfo: nil];

  [t _setName: [t name]];

  [t main];

  [NSThread exit];
  // Not reached
  return NULL;
}


- (void) main
{
  if (_active == NO)
    {
      [NSException raise: NSInternalInconsistencyException
                  format: @"[%@-%@] called on inactive thread",
        NSStringFromClass([self class]),
        NSStringFromSelector(_cmd)];
    }

  [_target performSelector: _selector withObject: _arg];
}


GSRunLoopThreadInfo *
GSRunLoopInfoForThread(NSThread *aThread)
{
  GSRunLoopThreadInfo   *info;

  if (aThread == nil)
    {
      aThread = GSCurrentThread();
    }
  if (aThread->_runLoopInfo == nil)
    {
      [gnustep_global_lock lock];
      if (aThread->_runLoopInfo == nil)
        {
          aThread->_runLoopInfo = [GSRunLoopThreadInfo new];
    }
      [gnustep_global_lock unlock];
    }
  info = aThread->_runLoopInfo;
  return info;
}

RunLoop相关的类

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

typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
};

CFRunLoopModeRef

CFRunLoopObserverRef

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
};
- (void)viewDidLoad {
    [super viewDidLoad];
    // 创建Observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry: {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopEntry - %@", mode);
                CFRelease(mode);
                break;
            }
                
            case kCFRunLoopExit: {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
                NSLog(@"kCFRunLoopExit - %@", mode);
                CFRelease(mode);
                break;
            }
                
            default:
                break;
        }
    });
    // 添加Observer到RunLoop中
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    // 释放
    CFRelease(observer);
}

RunLoop的运行逻辑

RunLoop的运行流程

void CFRunLoopRun(void) { 
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
    // 通知Observers:进入Loop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    // 核心的Loop逻辑
    __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    // 通知Observers:退出Loop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
    
    return result;
}

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    do {
        // 通知Observer:即将处理timers
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        // 通知Observer:即将处理Sources
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        // 处理Block
        __CFRunLoopDoBlocks(rl, rlm);
        // 处理Source0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            // 处理Block
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
        // 判断是否有Source1,如果有,就跳转到handle_msg
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            goto handle_msg;
        }
        didDispatchPortLastTime = false;
        
        // 通知Observers:即将休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        // 进入休眠,等待其他消息唤醒
        __CFRunLoopSetSleeping(rl);
        __CFPortSetInsert(dispatchPort, waitSet);
        
        do {
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
            
        } while (1);
        
        // 通知
        __CFPortSetRemove(dispatchPort, waitSet);
        __CFRunLoopSetIgnoreWakeUps(rl);
        
        // 通知Observer:已经唤醒
        __CFRunLoopUnsetSleeping(rl);
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
    handle_msg: // 看看是谁唤醒了RunLoop,进行相应的处理
        __CFRunLoopSetIgnoreWakeUps(rl);
        if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            // 被timer唤醒的,处理timer
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        else if (livePort == dispatchPort) { // 被GCD唤醒
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            
        } else {// 被Source1唤醒的
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
        }
        // 执行Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        // 根据之前的执行结果,决定怎么做
        if (sourceHandledThisLoop && stopAfterHandle) {
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            retVal = kCFRunLoopRunFinished;
        }
        
        
    } while (0 == retVal);
    
    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }
    
    return retVal;
 }

RunLoop休眠的实现原理

RunLoop在实际开中的应用

线程保活


@interface MJPermenantThread()
@property (strong, nonatomic) MJThread *innerThread;
@property (assign, nonatomic, getter=isStopped) BOOL stopped;
@end

@implementation MJPermenantThread
#pragma mark - public methods
- (instancetype)init
{
   if (self = [super init]) {
       self.stopped = NO;
       __weak typeof(self) weakSelf = self;
       self.innerThread = [[MJThread alloc] initWithBlock:^{
//  往RunLoop里面添加Source\Timer\Observer, 
           [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
// while里面的代码不会多次执行,因为runloop已经让线程休眠了
           while (weakSelf && !weakSelf.isStopped) {
               [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
           }
  // NSRunLoop的run方法是无法停止的,它专门用于开启一个永不销毁的线程(NSRunLoop)
       //        [[NSRunLoop currentRunLoop] run];
       }];
       [self.innerThread start];
   }
   return self;
}

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

- (void)stop {
   if (!self.innerThread) return;
   //  在子线程调用stop(waitUntilDone设置为YES,代表子线程的代码执行完毕后,这个方法才会往下走)
   [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}

- (void)dealloc {
   NSLog(@"%s", __func__);
   [self stop];
}

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

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

RunLoop监听页面卡顿

void runLoopOberCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    RunLoopObserverViewController *monitor = (__bridge RunLoopObserverViewController*)info;
    monitor->activity = activity;
    long st = dispatch_semaphore_signal(monitor->semaphore);
    NSLog(@"dispatch_semaphore_signal:st=%ld,time:%@",st,[monitor getCurTime]);
    if (activity == kCFRunLoopEntry) {  // 即将进入RunLoop
        NSLog(@"runLoopObserverCallBack - %@",@"kCFRunLoopEntry");
    } else if (activity == kCFRunLoopBeforeTimers) {    // 即将处理Timer
        NSLog(@"runLoopObserverCallBack - %@",@"kCFRunLoopBeforeTimers");
    } else if (activity == kCFRunLoopBeforeSources) {   // 即将处理Source
        NSLog(@"runLoopObserverCallBack - %@",@"kCFRunLoopBeforeSources");
    } else if (activity == kCFRunLoopBeforeWaiting) {   //即将进入休眠
        NSLog(@"runLoopObserverCallBack - %@",@"kCFRunLoopBeforeWaiting");
    } else if (activity == kCFRunLoopAfterWaiting) {    // 刚从休眠中唤醒
        NSLog(@"runLoopObserverCallBack - %@",@"kCFRunLoopAfterWaiting");
    } else if (activity == kCFRunLoopExit) {    // 即将退出RunLoop
        NSLog(@"runLoopObserverCallBack - %@",@"kCFRunLoopExit");
    } else if (activity == kCFRunLoopAllActivities) {
        NSLog(@"runLoopObserverCallBack - %@",@"kCFRunLoopAllActivities");
    }
}

/**
 # RunLoop处理事件的两个主要阶段
     kCFRunLoopBeforeSouces和KCFRunLoopBeforeWating之间
     KCFRunLoopAfterwaiting之后
    - 如果每次检测runloop的状态一直停留在 kCFRunLoopBeforeSouces 或者 KCFRunLoopAfterwaiting,则runloop一直在处理事件(source0 source1)
 
 */
- (void)startMonitor {
    semaphore = dispatch_semaphore_create(1);
    CFRunLoopObserverContext context = { 0 , (__bridge  void*)self, NULL, NULL };
    obsever =  CFRunLoopObserverCreate(kCFAllocatorDefault,
                                       kCFRunLoopAllActivities,
                                       true,
                                       0,
                                       runLoopOberCallback,
                                       &context);
    CFRunLoopAddObserver(CFRunLoopGetMain(), obsever, kCFRunLoopCommonModes);
    
    // 在子线程中监控时长
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            // dispatch_semaphore_wait 使信号量减一,如果超时了,则返回了一个非0的值,然后往下执行
            // dispatch_semaphore_wait 往下执行的条件 减一之后,信号量不为0 或 等待超时
            // runloop状态改变,使用信号量 +1, 这边wait之后,信号量不为0,要么是在runloop不断改变状态,要么就是等待超时了
            long st = dispatch_semaphore_wait(self->semaphore, dispatch_time(DISPATCH_TIME_NOW, 3 *NSEC_PER_MSEC));
            if (st != 0) { // 信号量超时了 - 即runloop的状态长时间没有发生变更,长期处于某一状态
                if (!self->obsever) {
                   self->timeoutCount = 0;
                    self->semaphore = 0;
                    self->activity = 0;
                    return;
                }
                if (self->activity == kCFRunLoopBeforeSources || self->activity == kCFRunLoopAfterWaiting) {
                    if (++self->timeoutCount < 5) {
                        continue; // 不足 5 次。直接 continue 当次循环,不将timtouotcount置为0
                    }
                    NSLog(@"-----卡顿了----上报栈信息");
                    self->timeoutCount = 0;
                }
            }
        }
    });
}


上一篇下一篇

猜你喜欢

热点阅读