iOS 涨薪: Run Loop 面试题

Run Loop

运行循环

app 程序只有不停地运行, 才能不断响应用户的操作

Run Loop 两大功能:

  • 睡眠中,等待消息
  • 处理消息

从睡眠中 -> 处理消息, 需要一个唤醒的过程

1、 讲讲 RunLoop, 项目中有用到吗?

RunLoop 的基本作用:

保持程序的持续运行

节省 CPU 的资源,提高程序的性能 ( 没有事情,就请休眠,不要功耗。有事情,就处理)

2、 RunLoop 内部实现逻辑?

  • Core Foundation 中关于 RunLoop 的 5 个类:

    CFRunLoopRef

    CFRunLoopModeRef

    CFRunLoopSourceRef

    CFRunLoopTimerRef

    CFRunLoopObserverRef

  • __CFRunLoop 的数据结构

struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;            /* locked for accessing mode list */
    __CFPort _wakeUpPort;            // used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    
    
    
    pthread_t _pthread;
    uint32_t _winthread;
    
    
    
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

在此一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这有个iOS交流群:642363427,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术!

3、 RunLoop 和线程的关系?

  • 每条线程,都有唯一的一个,与之对应的 RunLoop 对象
    runloops[thread] = runloop

  • RunLoop 保存在一个全局的 Dictionary 里,线程作为 Key, RunLoop 作为 Value

  • 线程刚创建时,并没有 RunLoop 对象, RunLoop 会在第一次获取它时,创建

  • RunLoop 会在线程结束时,销毁线程都没有了,runloop 也就没有意义了

子线程,要什么 runloop?

没有 runloop , 就是命令行,调用一次就完结

有了 runloop, 可以反复休眠、唤醒、处理消息

  • 主线程的 RunLoop 已经自动获取 ( 创建 ),子线程默认没有开启 RunLoop
    子线程中,获取一下 currentRunLoop, 就创建开启了 RunLoop
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    
    // 从这里拿
    return _CFRunLoopGet0(pthread_self());
}

进入详情

// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    if (pthread_equal(t, kNilPthreadT)) {
    t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
        
        
     // 从字典里面,获取 runloop 对象
    // __CFRunLoops 字典
    // pthreadPointer(t) ,键 key
    
    
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    
    // runloop 对象 loop 不存在,就新建
    if (!loop) {
            // 字典中,设置
            //   __CFRunLoops  字典
            //   pthreadPointer(t),键 key
            //   newLoop, 值  value
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
        loop = newLoop;
    }
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __CFUnlock(&loopsLock);
    CFRelease(newLoop);
    }
    if (pthread_equal(t, pthread_self())) {
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    return loop;
}

4、 timer 和 runloop 的关系?

5、 程序中添加每 3 秒响应一次的 NSTimer, 当拖动 tableView 时 timer 可能无法响应,要怎么解决?

6、 RunLoop 是怎么响应用户操作的,具体流程是什么样的?

7、 说说 RunLoop 的几种状态

8、 runloop 的 mode 作用是什么?

推荐👇:

如果你想一起进阶,不妨添加一下交流群642363427