摘要:本文将带你了解IOS开发入门iOS 开发runLoop 机制详解,希望本文对大家学IOS有所帮助。
本文将带你了解IOS开发入门iOS 开发runLoop 机制详解,希望本文对大家学IOS有所帮助。
NSRunLoop机制
对于iOS开发,runLoop机制还是很有必要了解一下的,最近在做一个广告图的功能正好需要了解下runtime机制问题,在查看了官方文档API以及论坛贴吧博客各位大牛的文章后,整理下关于我自己的理解和总结.
首先说介绍下NSRunLoop
OSX/iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。
CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。
NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。
NSRunLoop 与线程的关系
其实runLoop就是一个do … while()函数,每个runLoop对应一个线程他们是一一对应的关系,关系保存在一个全局的Dictionary里边,线程刚创建时没有RunLoop,如果不主动获取,是不会有的,RunLoop的创建发生在第一次获取时,RunLoop的销毁发生在线程结束,只能在一个线程的内部获取它的RunLoop(主线程除外)主线程默认有个RunLoop.
Thread包含一个CFRunLoop,一个CFRunLoop包含一种CFRunLoopMode,mode包含CFRunLoopSource,CFRunLoopTimer和CFRunLoopObserver。
RunLoop只能运行在一种mode下,如果要换mode当前的loop也需要停下重启成新的。利用这个机制,ScrollView过程中NSDefaultRunLoopMode的mode会切换UITrackingRunLoopMode来保证ScrollView的流畅滑动不受只能在NSDefaultRunLoopMode时处理的事件影响滑动。同时mode还是可定制的。
NSDefaultRunLoopMode:默认,空闲状态
UITrackingRunLoopMode:ScrollView滑动时
UIInitializationRunLoopMode:启动时
NSRunLoopCommonModes:Mode集合 Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes来解决
//然后再添加到NSRunLoopCommonModes里
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(timerTick:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 访问 loopsDic 时的锁
static CFSpinLock_t loopsLock;
/// 获取一个 pthread 对应的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
/// 直接从 Dictionary 里获取。
CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
if (!loop) {
/// 取不到时,创建一个
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
/// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
return _CFRunLoopGet(pthread_self());
}
介绍完线程和RunLoop的关系后,主要看下RunLoop和RunLoopModel,
RunLooprunLoop 在CoreFoundation对外一共有五大类最为对外的接口:
RunLoop
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
1.其中 CFRunLoopModeRef 类并没有对外暴露,只是通过 CFRunLoopRef 的接口进行了封装
一个 RunLoZ喎?"/kf/ware/vc/" target="_blank" class="keylink">vcCCw/LqsyPS4ybj2IE1vZGWjrMO/uPYgTW9kZSDT1rD8uqzI9LjJuPYgU291cmNlL1RpbWVyL09ic2VydmVyoaPDv7TOtffTwyBSdW5Mb29wILXE1ve6r8r9yrGjrNa7xNzWuLaoxuTW0NK7uPYgTW9kZaOs1eK49k1vZGWxu7PG1/cgQ3VycmVudE1vZGWho8jnufvQ6NKqx9C7uyBNb2Rlo6zWu8Tczcuz9iBMb29wo6zU2dbY0MLWuLao0ru49iBNb2RlIL34yOuho9Xi0fnX9tb30qrKx86qwcu31rj0v6qyu82s1+m1xCBTb3VyY2UvVGltZXIvT2JzZXJ2ZXKjrMjDxuS7pbK707DP7KGjPC9wPg0KPGg0IGlkPQ=="2-cfrunloopsourceref">2. CFRunLoopSourceRef
是事件产生的地方,Source包含了两个部分:Source0 和 Source1。
source0:处理如UIEvent,CFSocket这样的事件
source1:Mach port驱动,CFMachport,CFMessagePort
Source0 只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
Source1 包含了一个 mach_port 和一个回调(函数指针),被用于和通过内核和其他 mach_port 相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,其原理在下面会讲到。
3. CFRunLoopTimerRef
NSTimer是对RunLoopTimer的封装
是基于时间的触发器,它和 NSTimer 是toll-free bridge 的。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调
4. CFRunLoopObserverRef
Cocoa框架中很多机制比如CAAnimation等都是由RunLoopObserver触发的。observer到当前状态的变化进行通知
观察者,每个 Ovserver 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:
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
};
RunLoopModel
CFRunLoopMode 和 CFRunLoop 的结构大致如下:
struct __CFRunLoopMode {
CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
CFMutableSetRef _sources0; // Set
CFMutableSetRef _sources1; // Set
CFMutableArrayRef _observers; // Array
CFMutableArrayRef _timers; // Array
...
};
struct __CFRunLoop {
CFMutableSetRef _commonModes; // Set
CFMutableSetRef _commonModeItems; // Set
CFRunLoopModeRef _currentMode; // Current Runloop Mode
CFMutableSetRef _modes; // Set
...
};
这里有个概念叫 “CommonModes”:一个 Mode 可以将自己标记为”Common”属性(通过将其 ModeName 添加到 RunLoop 的 “commonModes” 中)。RunLoop 会自动将 _commonModeItems 加入到具有 “Common” 标记的所有Mode里。
应用场景举例:主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为”Common”属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。
有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。
RunLoop底层实现
从上面代码可以看到,RunLoop的核心是基于 mach port 的,其进入休眠时调用的函数是 mach_msg()。为了解释这几个概念,下面稍微介绍一下OSX/iOS的系统架构。
苹果官方将整个系统大致划分为上述4个层次:
应用层包括用户能接触到的图形应用,例如 Spotlight、Aqua、SpringBoard等。
应用框架层即开发人员接触到的 Cocoa 等框架。
核心框架层包括各种核心框架、OpenGL 等内容。
Darwin 即操作系统的核心,包括系统内核、驱动、Shell 等内容,这一层是开源的,其所有源码都可以在opensource.apple.com里找到。
CFRunLoop {
current mode = kCFRunLoopDefaultMode
common modes = {
UITrackingRunLoopMode
kCFRunLoopDefaultMode
}
common mode items = {
// source0 (manual)
CFRunLoopSource {order =-1, {
callout = _UIApplicationHandleEventQueue}}
CFRunLoopSource {order =-1, {
callout = PurpleEventSignalCallback }}
CFRunLoopSource {order = 0, {
callout = FBSSerialQueueRunLoopSourceHandler}}
// source1 (mach port)
CFRunLoopSource {order = 0, {port = 17923}}
CFRunLoopSource {order = 0, {port = 12039}}
CFRunLoopSource {order = 0, {port = 16647}}
CFRunLoopSource {order =-1, {
callout = PurpleEventCallback}}
CFRunLoopSource {order = 0, {port = 2407,
callout = _ZL20notify_port_callbackP12__CFMachPortPvlS1_}}
CFRunLoopSource {order = 0, {port = 1c03,
callout = __IOHIDEventSystemClientAvailabilityCallback}}
CFRunLoopSource {order = 0, {port = 1b03,
callout = __IOHIDEventSystemClientQueueCallback}}
CFRunLoopSource {order = 1, {port = 1903,
callout = __IOMIGMachPortPortCallback}}
// Ovserver
CFRunLoopObserve
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号