iOS进阶-Runloop源码探究

1.do-while 循环


源码

void CFRunLoopRun(void) { /* DOES CALLOUT */

int32_t result;

do {

result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);

CHECK_FOR_FORK();

} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);

}

结论

可以看到,runloop在run起来后其实是用一个do-while循环实现的,不同的是,runloop可以做到不需要处理事务的时候就sleep,需要的时候就work。其作用总结就是:

  • 保持程序的持续运行

  • 处理App中各种事件(触摸、定时器、performSelector)

  • 节省CPU资源、提供程序的性能:该做事做事,该休息休息

2.与线程的关系


源码

##### mainRunloop

CFRunLoopRef mainRunloop = CFRunLoopGetMain();

##### CFRunLoopGetMain()函数内部实现

CFRunLoopRef CFRunLoopGetMain(void) {

CHECK_FOR_FORK();

static CFRunLoopRef __main = NULL; // no retain needed

//可以看到传了一个主线程参数进入

if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed

return __main;

}

##### _CFRunLoopGet0()函数内部实现

CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {

if (pthread_equal(t, kNilPthreadT)) {

t = pthread_main_thread_np();

}

__CFSpinLock(&loopsLock);

if (!__CFRunLoops) {

__CFSpinUnlock(&loopsLock);

//建立全局的字典用来存储 线程和runloop的关系

CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);

//通过__CFRunLoopCreate()函数创建runloop实例对象

CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());

//将mainLoop 和 mainThreadb存入字典

CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);

if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {

CFRelease(dict);

}

CFRelease(mainLoop);

__CFSpinLock(&loopsLock);

}

//主线程的loop在上面就已经创建了,所以如果获取不到,那肯定是子线程的loop

CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

__CFSpinUnlock(&loopsLock);

//没有loop,那就创建子线程的loop对象

if (!loop) {

CFRunLoopRef newLoop = __CFRunLoopCreate(t);

__CFSpinLock(&loopsLock);

loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

if (!loop) {

//子线程和其runloop也会被存在这个全局的字典中

CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);

loop = newLoop;

}

// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it

__CFSpinUnlock(&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);

}

}

//返回runloop

return loop;

}

结论

  • 系统会创建一个全局的可变字典CFMutableDictionaryRef dict,储存线程和runloop;

  • 主线程的runloop是默认创建的

  • 子线程的runloop默认不启动,需要主动获取

3.内部的mode-item关系


源码

#### __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;

//有好多的Modes、Items

CFMutableSetRef _commonModes;

CFMutableSetRef _commonModeItems;

//但是只有一个currentMode

CFRunLoopModeRef _currentMode;

CFMutableSetRef _modes;

struct _block_item *_blocks_head;

struct _block_item *_blocks_tail;

CFTypeRef _counterpart;

};

struct __CFRunLoopMode {

CFRuntimeBase _base;

pthread_mutex_t _lock; /* must have the run loop locked before locking this */

CFStringRef _name;

Boolean _stopped;

char _padding[3];

//有好多的事件源 _sources0、_sources1、_observers、_timers

CFMutableSetRef _sources0;

CFMutableSetRef _sources1;

CFMutableArrayRef _observers;

CFMutableArrayRef _timers;

CFMutableDictionaryRef _portToV1SourceMap;

__CFPortSet _portSet;

CFIndex _observerMask;

#if USE_DISPATCH_SOURCE_FOR_TIMERS

dispatch_source_t _timerSource;

dispatch_queue_t _queue;

Boolean _timerFired; // set to true by the source when a timer has fired

Boolean _dispatchTimerArmed;

#endif

#if USE_MK_TIMER_TOO

mach_port_t _timerPort;

Boolean _mkTimerArmed;

#endif

#if DEPLOYMENT_TARGET_WINDOWS

DWORD _msgQMask;

void (*_msgPump)(void);

#endif

uint64_t _timerSoftDeadline; /* TSR */

uint64_t _timerHardDeadline; /* TSR */

};

结论

  • __CFRunLoop对象内部包含多个modes

  • 而每个mode中又包含了多个items

  • items就是事件源_sources0、_sources1、_observers、_timers...

  • 但是__CFRunLoop对象同时只能持有一种mode:_currentMode

runloop对象结构图

4.Runloop的item


runloop有六大事务item,这些item都是由runloop调用的。先看代码

- (void)timerItem{

//__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__

[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {

NSLog(@"test timer");

}];

}

等程序运行起来在断点看内存堆栈调用

(lldb) bt

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2

* frame #0: 0x000000010b59a240 01-Runloop初探`__28-[ViewController sourceDemo]_block_invoke(.block_descriptor=0x0000600000658318, timer=0x0000600000f58010) at ViewController.m:26

frame #1: 0x000000010b987413 Foundation`__NSFireTimer + 72

frame #2: 0x000000010bfb3b94 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20

frame #3: 0x000000010bfb3882 CoreFoundation`__CFRunLoopDoTimer + 1026

frame #4: 0x000000010bfb2eda CoreFoundation`__CFRunLoopDoTimers + 266

frame #5: 0x000000010bfadc4e CoreFoundation`__CFRunLoopRun + 2238

frame #6: 0x000000010bfad066 CoreFoundation`CFRunLoopRunSpecific + 438

frame #7: 0x00000001154c6bb0 GraphicsServices`GSEventRunModal + 65

frame #8: 0x000000010f2e0d4d UIKitCore&#

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值