码迷,mamicode.com
首页 > 其他好文 > 详细

runloop底层实现(二)mode

时间:2016-08-02 20:48:40      阅读:200      评论:0      收藏:0      [点我收藏+]

标签:

本文重点讨论到底什么是runloopmode及业务逻辑。

 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;

    CFTypeRef _counterpart;

};

其中CFRunLoopModeRef _currentMode;是对runloopmode的定义。

通过currentMode这个名字即可以看出runloop的mode是可以改变的。

而CFMutableSetRef _commonModes;是整个runloop所包含的mode的集合,请注意这里的_commonModes并不是等价于

kCFRunLoopCommonModes

 

先看CFRunLoopModeRef的结构:

struct __CFRunLoopMode {

    CFRuntimeBase _base;

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

    CFStringRef _name;

    Boolean _stopped;

    char _padding[3];

    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 */

};

其中:

    CFMutableSetRef _sources0;

    CFMutableSetRef _sources1;

    CFMutableArrayRef _observers;

    CFMutableArrayRef _timers;

分别对应着runloop中常常提到的三种source、observer、timer也就是数组,存放了这三种结构。

通过CFRunLoopAddSource、CFRunLoopAddObserver、CFRunLoopAddTimer分别添加。

代码如下:

void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */

    CHECK_FOR_FORK();

    if (__CFRunLoopIsDeallocating(rl)) return;

    if (!__CFIsValid(rls)) return;

    Boolean doVer0Callout = false;

    __CFRunLoopLock(rl);

//当添加的source注册到了commonmodes的时候

    if (modeName == kCFRunLoopCommonModes) {

CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;

if (NULL == rl->_commonModeItems) {

//将source增加到_commonModeItems

    rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);

}

CFSetAddValue(rl->_commonModeItems, rls);

if (NULL != set) {

    CFTypeRef context[2] = {rl, rls};

    /* add new item to all common-modes */

//将新增的source同步到_commonModes的所有mode中

    CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);

    CFRelease(set);

}

    } else {

......

}

}

简单来说就是如果一个source注册到了kCFRunLoopCommonModes就相当于注册到了所有在_commonModes数组里的mode之中,_commonModes里默认有两种mode::kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。UITrackingRunLoopMode是scrollview滑动时所在的mode,因此如果在滑动scrollview需要保证图片下载,音频播放等source的性能,将这些source注册为kCFRunLoopCommonModes即可。

 

而source到底是什么呢,在代码中可以找到如下定义:

struct __CFRunLoopSource {

    CFRuntimeBase _base;

    uint32_t _bits;

    pthread_mutex_t _lock;

    CFIndex _order; /* immutable */

    CFMutableBagRef _runLoops;

    union {

        CFRunLoopSourceContext version0; /* immutable, except invalidation */

        CFRunLoopSourceContext1 version1; /* immutable, except invalidation */

    } _context;

};

CFRunLoopSourceContext定义如下:

typedef struct {

    CFIndex version;

    void * info;

    const void *(*retain)(const void *info);

    void (*release)(const void *info);

    CFStringRef (*copyDescription)(const void *info);

    Boolean (*equal)(const void *info1, const void *info2);

    CFHashCode (*hash)(const void *info);

    void (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);

    void (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);

    void (*perform)(void *info);

} CFRunLoopSourceContext;

其中的void (*perform)(void *info);便是soure回调方法的地址。

runloop对source调用的过程如下:

/* rl, rlm are locked on entrance and exit */

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {

......

 

        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);

        if (sourceHandledThisLoop) {

            __CFRunLoopDoBlocks(rl, rlm);

        }

......

            

         CFRUNLOOP_WAKEUP_FOR_SOURCE();

         // Despite the name, this works for windows handles as well

         CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);

         if (rls) {

              mach_msg_header_t *reply = NULL;                

              sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) ||   sourceHandledThisLoop;          

......

}

 

 

static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) { /* DOES CALLOUT */

......

                        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);

......

}

 

 

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info) {

    if (perform) {

        perform(info);

    }

    getpid(); // thwart tail-call optimization

}

 

 

runloop底层实现(二)mode

标签:

原文地址:http://www.cnblogs.com/ThreeLittlePigs/p/5730413.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!