runloop学习记录

1.runloop源码

源码

Snip20180407_10.png


2.对象介绍

image.png

接下来的对象介绍,可以借助这个图理清对象间的关系

// 一个runloop对象
typedef struct __CFRunLoop * CFRunLoopRef;
// source
typedef struct __CFRunLoopSource * CFRunLoopSourceRef;
// observer观察者
typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;
// timer
typedef struct __CFRunLoopTimer * CFRunLoopTimerRef;
// mode
typedef struct __CFRunLoopMode *CFRunLoopModeRef;

2.1. CFRunLoopRef

一个指向__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;
};

从这个结构体的内容可以看出:

  • 一个runloop对象主要包含modes、线程
  • modes是一个可变集合,里面存着mode,所以一个runloop对象可以包含多个mode
  • 一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。(这三个的概念后面讲)。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。

2.2. CFRunLoopSourceRef

一个指向__CFRunLoopSource结构体的指针

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;
};
  • 结构体有一个_runLoops(可变包对象),说明一个source可以添加到多个runloop中

  • CFRunLoopSourceContext version0

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;

  • CFRunLoopSourceContext1 version1
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);
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
    mach_port_t (*getPort)(void *info);
    void *  (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
    void *  (*getPort)(void *info);
    void    (*perform)(void *info);
#endif
} CFRunLoopSourceContext1;
  • version0和version1区别
    当我们接受到的消息(source),比如触摸事件,滑动事件等等;这个source是有两种的,一种是version0,也就是用source0来表示,一种是version1 -> source1
    区别
version0 / source0 source0 是非基于 port 的事件,主要是 APP 内部事件,如点击事件,触摸事件等
version1 / source1 source1 是基于Port的,通过内核和其他线程通信,接收,分发系统事件。

2.3. CFRunLoopObserverRef

一个指向__CFRunLoopObserver结构体的指针
作用:观察者,观察runloop的各种状态,并通过回调抛出去

struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount;
    CFOptionFlags _activities;      /* immutable */
    CFIndex _order;         /* immutable */
    CFRunLoopObserverCallBack _callout; /* immutable */
    CFRunLoopObserverContext _context;  /* immutable, except invalidation */
};

其中的CFOptionFlags是一个枚举:表示runloop的状态

/* Run Loop Observer Activities */
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    // 占位
};


CFRunLoopObserverRef观察者会将观察到的状态变化通过回调_callout跑出去
看下这个CFRunLoopObserverCallBack _callout

// 这个回调可以将观察者、runloop状态、info传出去
typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);

2.4. CFRunLoopTimerRef

是一个指向__CFRunLoopTimer结构体的指针

某些数据类型能够在Core Foundation和Foundation之间互换使用,可被互换使用的数据类型被称为Toll-Free Bridged类型。

  • CFRunLoopTimerRef 是定时器,可以在设定的时间点抛出回调
  • CFRunLoopTimerRef和NSTimer是toll-free bridged的,可以相互转换。
struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;       /* immutable */
    CFTimeInterval _tolerance;          /* mutable */
    uint64_t _fireTSR;          /* TSR units */
    CFIndex _order;         /* immutable */
    CFRunLoopTimerCallBack _callout;    /* immutable */
    CFRunLoopTimerContext _context; /* immutable, except invalidation */
};

2.5.CFRunLoopModeRef

一个指向__CFRunLoopMode结构体的指针

typedef struct __CFRunLoopMode *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 */
};

  • _sources0、 _sources1都是可变集合对象,对应着用来存取CFRunLoopSourceRef对象,CFRunLoopSourceRef对象中有version0version1分别对应着_sources0_sources1
  • 包含了observer、timer
  • CFStringRef _name就是mode的名字,如:kCFRunLoopDefaultMode
  • 有几种mode图片来源
    image.png

3.函数介绍

3.1.__CFRunLoopDoObservers

通知Observer,runloop要做什么事情
这个__CFRunLoopDoObservers函数需要传三个参数,分别是

  • CFRunLoopRef(runloop对象)
  • CFRunLoopModeRef(runloop的mode)
  • CFRunLoopActivity(runloop的状态枚举)


    这个函数的实现

3.2._CFRunLoopGet0

runloop对象是存在全局字典中的,key就是pthread_t
这个_CFRunLoopGet0函数的作用就是获取对应线程的runloop

实现思路(依据就是下面截图的源码)
1.先判断这个全局字典存不存在,不存在,创建一个,并将主线程的runloop加进去
2.直接去字典里取这个loop
3.如果loop不存在,就创建一个loop加入到全局字典中
// 伪代码
if(!__CFRunLoops) {
      1.创建全局字典
      2.创建主线程loop,并加入到全局字典中
}
根据线程pthread_t为key,去字典取对应的loop
if(!loop) {
      1.创建loop
      2.加入到字典中
}
return loop

其实这个说明了runloop和线程是一一对应的关系

Snip20180407_22.png

3.3.获取主线程loop和获取当前的loop

image.png

参考文档

强力推荐
RunLoop系列之要点提炼
RunLoop系列之源码分析
iOS刨根问底-深入理解RunLoop
深入理解 RunLoop

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 1 Runloop机制原理 深入理解RunLoop http://www.cocoachina.com/ios/2...
    Kevin_Junbaozi阅读 3,996评论 4 30
  • RunLoop 的概念 一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线...
    Mirsiter_魏阅读 617评论 0 2
  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    made_China阅读 1,204评论 0 7
  • 转载:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling阅读 1,436评论 0 13
  • 初中和高中时总说,要珍惜身边每一位朋友。 那时的我们,还不知道什么是珍惜,因为也不知道什么是失去。对身边的人好,经...
    长亭微雨阅读 242评论 0 0