runloop是一个看似很神秘的东西,其实一点也不神秘。每个线程都有一个实际已经存在的runloop。比如我们的主线程,在主函数的UIApplication中:

UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]))


系统就为我们将主线程的main runloop隐式的启动了。runloop顾名思义就是一个“循环”,他不停地运行,从程序开始到程序退出。正是由于这个“循环”在不断地监听各种事件,程序才有能力检测到用户的各种触摸交互、网络返回的数据才会被检测到、定时器才会在预定的时间触发操作……

runloop只接受两种任务:输入源和定时源。本文中说的就是定时源。默认状态下,子线程的runloop中没有加入我们自己的源,那么我们在子线程中使用自己的定时器时,就需要自己加到runloop中,并启动该子线程的runloop,这样才能正确的运行定时器。

每一个线程都有其对应的RunLoop,但是默认非主线程的RunLoop是没有运行的,需要为RunLoop添加至少一个事件源,然后去run它。一般情况下我们是没有必要去启用线程的RunLoop的,除非你在一个单独的线程中需要长久的检测某个事件。

a 主线程的runloop自动创建,子线程的runloop默认不创建(在子线程中调用NSRunLoop *runloop = [NSRunLoop currentRunLoop];

获取RunLoop对象的时候,就会创建RunLoop);

b runloop退出的条件:app退出;线程关闭;设置最大时间到期;modeItem为空;

c 同一时间一个runloop只能在一个mode,切换mode只能退出runloop,再重进指定mode(隔离modeItems使之互不干扰);

d 一个item可以加到不同mode;一个mode被标记到commonModes里(这样runloop不用切换mode)

我们通常在主线程中使用NSTimer,有个实际遇到的问题需要注意。当滑动界面时,系统为了更好地处理UI事件和滚动显示,主线程runloop会暂时停止处理一些其它事件,这时主线程中运行的NSTimer就会被暂停。解决办法就是改变NSTimer运行的mode(mode可以看成事件类型),不使用缺省的NSDefaultRunLoopMode,而是改用NSRunLoopCommonModes,这样主线程就会继续处理NSTimer事件了。具体代码如下:

NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timer:)userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];



  • 自动释放池
RunLoop下也有自己的自动释放池,用于保存RunLoop下的变量等。
    //Runloop的自动释放池是什么时候创建和销毁的
     第一次创建:当runloop开启的时候
     最后一次销毁:kCFRunLoopExit 当runloop退出的时候
     其他:当runloop即将休眠的时候会把之前的自动释放池销毁,然后创建一个新的自动释放池



Runloop运行逻辑