iOS-Runloop---随笔

前言

之前一直不知道什么是runloop,开发中也没怎么用到,直到最近开发的一个项目,同事在写计时的时候用NSTimer,在哪里边用到了runloop,才关注了一下。然后查了好多资料,看了好多博客把自己的学习所得记录下来,有什么不足或者错误地方,请大家多多指正,互相学习。

Runloop 是什么?

Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none.

翻译:Run loops是线程的基础架构部分。一个runloop就是一个事件处理循环,用来不停的调配工作以及处理输入事件。使用runloop的目的是使你的线程在有工作的时候工作,没有的时候休眠。

Runloop基本作用

1.保持程序持续运行
2.处理程序中的各种事件(例如:触摸事件、定时器事件(NSTimer)、选择器事件(selecter))等等.
2.3 节省CPU资源,提高程序性能。runloop能让主线程有事情处理事情,没事情时候,主线程处于休眠状态.

……..
假如没有runloop,我们程序执行完后,就会立马退出。打开xcode,创建一个空的项目,找到main.m文件,把程序入口main()函数改成如下:

int main(int argc, char * argv[]) { 
@autoreleasepool {
NSLog(@"runloop over");
 return 0;
}

上面这段修改后的main()函数,我们再来运行程序, 程序运行到return 0,就立马结束了。与之相对应的,xcode模拟器也就关闭了界面。

Runloop 核心实现

runloop核心代码实现大致如下
int main(int argc, char * argv[]) {
BOOL running = YES;
do{
//执行各种任务,处理各种事件
}while(running)
return 0;
}

关于runloop核心部分就是这样的一个do-while循环函数,程序开始后,就一直在do-while里边运行,不会执行到 return 0这一行代码;想详细了解这部分实现的朋友可以看看这篇博客,里边有关于这部分的详细说明。

main()函数中的runloop

在我们创建一个工程时候,每一个程序都以一个入口函数int main(int argc, char * argv[]),程序自动帮我们runloop所以程序能够持续运行,不会退出。
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

其实在UIApplicationMain()函数内部就帮我们创建了一个runloop对象,所以UIApplicationMain()没有返回值,保持了我们程序的持续运行。有心的朋友可以在 UIApplicationMain()函数前面和后面都加上NSlog输出一下,你就会发现, UIApplicationMain()前面的NSlog会打印输出,而UIApplicationMain()函数后面的NSlog是不会打印输出的。UIApplicationMain()函数里边的runloop相当于一个死循环的循环,并且这个默认启动的runloop是跟主线程相关联的,处理主线程相关的各种事件。

Runloop对象

iOS中有两套API来访问和使用Runloop

  1. Foundation框架下(基于OC)
    1. NSRunLoop:是基于CFRunLoopRef的一层包装
    2. Foundation获取runloop
      4. 获取当前的runloop 可以调用[NSRunLoop currentRunLoop];
      5. 获取主线程的runloop 可以调用[NSRunLoop mainRunLoop];
  2. Core Foundation框架下(基于C语言)
    1. CFRunLoopRef
    2. Core Foundation获取runloop
      4. 获取当前的runloop 可以调用CFRunLoopGetCurrent();
      5. 获取主线程的runloop 可以调用CFRunLoopGetMain();;
  3. runloop对象的创建

    1. 创建一个runloop对象是通过[NSRunLoop currentRunLoop];来创建的关于runloop底层实现具体可以看看_CFRunLoopGet()函数内部实现。_CFRunLoopGet()函数实现源码如下(以下代码摘自这里):
      `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,然后创建其他线程的runloop对象。runloop对象通过字典来存储,每一个key对应一个线程,一个线程对应一个runloop。
6. CFRunLoopRef 的代码是开源的,具体函数可以去这里下载

Runloop与线程

每条线程都有一个唯一的与之对应的runloop对象,主线程的Runloop对象已经自动创建好了,不用手动创建,就在UIApplicationMain()函数里边;子线程的需要手动去创建。runloop在第一次启动时候创建,在线程结束的时候自动销毁。

Runloop相关的类

Core Foundation中关于runloop的类有5个:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef:事件源(输入源)
CFRunLoopTimerRef:基于事件的触发器
CFRunLoopObserverRef

CFRunLoopModeRef :表示runloop的运行模式,runloop要跑起来,必须得定义一个model,这个model称作currentModel。一个runloop对象可以有多个model,这些model可以切换;每个model又包含多个 Source/Timer/Observer,如果要切换model,必须先退出当前的loop,在指定一个model重新进入。这样做的目的是为了区分不同model下的Source/Timer/Observer,让他们不相互影响。每一个runloop默认情况下启用默认的kCFRunLoopDefaultMode模式,
系统默认有5种model

  1. kCFRunLoopDefaultMode:App默认的model,通常主线程在这个默认model下运行
  2. UITrackingRunLoopMode:跟踪界面的model,用于ScrollView追踪、触摸、滑动保证界面滑动时不受其他model影响。
  3. kCFRunLoopCommonModes:占位用的model,不是真正的model
  4. UIInitializationRunLoopMode:刚启动app时候启用的第一个model,启动完就不再使用的model。
  5. GSEventReceiveRunLoopMode:接受系统内部事件的model,通常不用

kCFRunLoopCommonModes使用:

NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(runloopModesTest) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
NSLog(@"---%@----",[NSRunLoop currentRunLoop]);

在项目上运行以上代码,在输出日志里边,有这些输出这里写图片描述从图片截取的输出可以很好的看出NSRunLoopCommonModes,就是kCFRunLoopDefaultMode模式和UITrackingRunLoopMode模式之间来回转换的一个桥梁。

  • CFRunLoopSourceRef按照官方文档分类:
    Port-Based Source:基于端口的事件源
    Custom Input Source:自定义的事件源
    Cocoa Perform Selecter Source
  • CFRunLoopSourceRef按照函数调用栈分类:
    Source0:
    Source1:基于port的,通过内核和其他线程通信,接收、分发系统事件

    CFRunLoopObserverRef:观察者,能够监听runloop状态的改变。可以监听时间点有如下几个:

/* 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//监听所有的runloop状态
};

由于水平有限,就写到这里了。有什么错误或者问题,请大家多多指正。后面几天会在我的git上陆续编写一些关于runloop简单实用的demo。有兴趣可以下载看看,更好理解runloop。

简单demo地址:https://github.com/hu1023186811/runloop-test.git

参考文献:

  1. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1
  2. http://www.cocoachina.com/ios/20150601/11970.html
  3. http://www.jianshu.com/p/fefe2ba87c68
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值