学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

RunLoop之简介、数据结构

RunLoop简介

运行循环,在程序运行过程中循环做一些事情(如接收消息、处理消息、休眠等待等);
RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象;
RunLoop不是一个简单的do...while循环,它涉及到用户态内核态之间的切换。

事件循环

事件循环就是对事件/消息进行管理,事件循环可以达到:

  • 没有消息需要处理时,休眠线程以避免资源占用。从用户态切换到内核态,等待消息;
  • 有消息需要处理时,立刻唤醒线程,回到用户态处理消息;
  • 通过调用mach_msg()函数来转移当前线程的控制权给内核态/用户态

RunLoop的基本作用

保持程序的持续运行:如果没有Runloop,main()函数一执行完,程序就会立刻退出(main()函数中调用了UIApplicationMain函数,它的内部会启动主线程的Runloop)

处理App中的各种事件(比如触摸事件、定时器事件等)

节省CPU资源,提高程序性能;该做事时做事,该休息时休息

应用范畴

定时器、PerformSelector
GCD:dispatch_async(dispatch_get_main_queue(), ^{ });
事件响应、手势识别、界面刷新
网络请求
AutoreleasePool

实际开发中的应用

使用端口或自定义输入源与其他线程进行通信
在子线程上使用定时器
解决NSTimer在滑动时停止工作的问题
控制线程的生命周期,实现一个常驻线程
在Cocoa应用程序中使用任何performSelector…方法
监控应用卡顿
性能优化

数据结构

CFRunLoopRef

RunLoop对象的底层就是一个CFRunLoopRef结构体,它里面存储者:

1
2
3
4
5
6
7
8
9
10
11
// CFRunLoop.h
typedef struct __CFRunLoop * CFRunLoopRef;
// CFRunLoop.c
struct __CFRunLoop {
pthread_t _pthread; // 与线程一一对应
CFMutableSetRef _commonModes;//存储着 NSString 对象的集合(Mode 的名称)
CFMutableSetRef _commonModeItems;//存储着被标记为通用模式的Source0/Source1/Timer/Observer
CFRunLoopModeRef _currentMode;//RunLoop当前的运行模式
CFMutableSetRef _modes;//存储着RunLoop所有的 Mode(CFRunLoopModeRef)模式
...
};

CFRunLoopModeRef

1
2
3
4
5
6
7
8
9
10
11
// CFRunLoop.h
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
// CFRunLoop.c
struct __CFRunLoopMode {
CFStringRef _name; // mode 类型,如:NSDefaultRunLoopMode
CFMutableSetRef _sources0; // CFRunLoopSourceRef
CFMutableSetRef _sources1; // CFRunLoopSourceRef
CFMutableArrayRef _observers; // CFRunLoopObserverRef
CFMutableArrayRef _timers; // CFRunLoopTimerRef
...
};

CFRunLoopModeRef代表RunLoop的运行模式
一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Souce1/Timer/Obsesrver;
RunLoop启动时只能选择其中一个Mode,作为currentMode;
如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入,切换模式不会导致程序退出
不同Mode中的Source0/Souce1/Timer/Observer能分隔开来,互不影响
如果Mode里没有任何Source0/Souce1/Timer/Observer,RunLoop会立马退出

常见模式

NSDefaultRunLoopMode、UITrackingRunLoopMode、UIRunLoopCommonModes(通用模式,默认包含defaultMode和TrackingMode,该模式不是实际存在的一种模式,它只是一个特殊的标记,是同步Source0/Souce1/Timer/Observer到多个Mode中的技术方案。被标记为通用模式的Source0/Source1/Timer/Observer都会存放到多个Mode中的技术方案。被标记为通用模式Source0/Source1/Timer/Observer都会存放到_commonModeItems集合中,会同步这些Source0/Source1/Timer/Observer到多个Mode中)

CFRunLoopSourceRef

在RunLoop中有两个很重要的概念,一个是上面提到的模式,还有一个就是事件源。事件源分为输入源和定时器源两种:

输入源又分为Source0和Source1两种,以下__CFRunLoopSource中的共用体uinon中的version0和version1就分别对应Source0和Source1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// CFRunLoop.h
typedef struct __CFRunLoopSource * CFRunLoopSourceRef;
// CFRunLoop.m
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;
};

Source0和Source1的区别

CFRunLoopTimerRef

CFRunloopTImerNSTimer是toll-free bridged的,可以相互转换;
performSelector:withObject:afterDelay:方法会创建timer并添加到RunLoop中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// CFRunLoop.h
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;
// CFRunLoop.c
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop; // 添加该 timer 的 RunLoop
CFMutableSetRef _rlModes; // 所有包含该 timer 的 modeName
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable 理想时间间隔 */
CFTimeInterval _tolerance; /* mutable 时间偏差 */
uint64_t _fireTSR; /* TSR units */
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; /* immutable 回调入口 */
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};

CFRunLoopObserverRef

作用

CFRunLoopObserverRef用来监听RunLoop的6种活动状态

1
2
3
4
5
6
7
8
9
10
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入 RunLoop
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理 Timers
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Sources
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出 RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU // 表示以上所有状态
};
  • UI刷新(BeforeWaiting)
  • Autorelease Pool(BeforeWaiting)

数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
// CFRunLoop.h
typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;
// CFRunLoop.c
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop; // 添加该 observer 的 RunLoop
CFIndex _rlCount;
CFOptionFlags _activities; /* immutable 监听的活动状态 */
CFIndex _order; /* immutable */
CFRunLoopObserverCallBack _callout; /* immutable 回调入口 */
CFRunLoopObserverContext _context; /* immutable, except invalidation */
};

CFRunLoopObserverRef中的_activities用来保存RunLoop的活动状态。当RunLoop的状态发生改变时,通过回调_callout通知所有监听这个状态的Observer