RunLoop总结

1.什么是runloop?

RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象。

事件循环

  • 没有消息需要处理时,休眠以避免资源占用


    RunLoop总结_第1张图片
    截图1.png
  • 有消息处理时,立刻被唤醒


    RunLoop总结_第2张图片
    截图2.png

main函数是为什么可以一直保证运行状态,而不退出 ?
在main函数当中,所调用的UIApplicationMain()内部会启动主线程的runloop,而runloop又是对事件循环的一种维护机制,可以做到在有事做的时候做事,没有事情做的时候,会通过从用户态到内核态的切换,从而避免资源的占用,让当前线程处于一种休眠状态。

2.数据结构

NSRunLoop是CFRunLoop的封装,提供了面向对象的API.

  • CFRunLoop
  • CGRunLoopMode
  • Source/Timer/Observer

CFRunLoop

RunLoop总结_第3张图片
截图3.png

CFRunLoopMode
RunLoop总结_第4张图片
截图4.png

CFRunLoopSource

  • source0
    需要手动唤醒线程
  • source1
    具备唤醒线程的能力

CFRunLoopTimer
基于事件的定时器

CFRunLoopObserver
观测时间点

  • kCFRunLoopEntry
  • kCFRunLoopBeforeTimers
  • kCFRunLoopBeforeSources
  • kCFRunLoopBeforeWaiting
  • kCFRunLoopAfterWaiting
  • kCFRunLoopExit

各个数据结构之间的关系

RunLoop总结_第5张图片
截图5.png

RunLoop的Mode
RunLoop总结_第6张图片
截图6.png

CommonMode的特殊性

NSRunLoopCommonModes

  • CommonMode不是实际存在的一种Mode。
  • 是同步Source/Timer/Observer到多个Mode中的一种技术解决方案

3.事件循环的实现机制

void CFRunLoopRun()

RunLoop总结_第7张图片
截图7.png

RunLoop总结_第8张图片
截图8.png

4.NSRunLoop与NSTimer

滑动TableView的时候我们的定时器还会生效吗?

RunLoop总结_第9张图片
截图9.png

解决方案
void CFRunLoopAddTimer(runLoop, timer,commonMode)

5.RunLoop与多线程

线程和RunLoop一一对应的。
自己创建的线程默认是没有RunLoop的

(1)怎样实现一个常驻线程?

a.为当前线程开启一个RunLoop;
[NSRunLoop currentRunLoop]或者CFRunLoopGetCurrent()
b.向该RunLoop中添加一个Port/Source等维护RunLoop的事件循环;
c.启动该RunLoop。

#import "MCObject.h"
@implementation MCObject
static NSThread *thread = nil;
// 标记是否要继续事件循环
static BOOL runAlways = YES;
+ (NSThread *)threadForDispatch{
    if (thread == nil) {
        @synchronized(self){
            if (thread == nil) {
                //线程的创建
                thread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequest) object:nil];
                [thread setName:@"com.imooc.thread"];
                //启动
                [thread start];
            }
        }
    }
    return thread;
}
- (void)runRequest{
    //创建一个source
    CFRunLoopSourceContext context = {0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
    //创建RunLoop,同时向RunLoop的DefaultMode下面添加Source
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
    //如果可以运行
    while (runAlways) {
        @autoreleasepool {
          //令当初RunLoop运行在DefaultMode下面
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10,true);
        }
    }
    //某一时机 静态变量 runAlways = NO时,可以保证跳出RunLoop,线程退出
    CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
    CFRelease(source);
}
@end

6.问题总结

(1)什么是RunLoop,它是怎样做到有事做事,没事休息的?

RunLoop实际是一个事件循环,用于处理事件和消息以及对它们进行管理。由于在调用CFRunLoopRun之后会调用系统的mach_msg()函数,同时发生了从用户态向核心态的切换,然后当前线程处于休眠状态,所以可以做到有事做时,做事没事做时休息。

(2)RunLoop与线程是怎样的关系?

a.runloop与线程是一一对应的关系
b.一个线程默认是没有runloop的,需要手动创建。

(3)实现一个常驻线程

我们可以通过三个步骤,第一个步骤呢,创建一个线程对应的runloop,之后向这个runloop中添加source、timer、observer、port等内容,第三步调用CFRunLoop的一个run方法就可以实现一个常驻线程。在具体使用的过程中,需要注意,运行的模式和资源添加的模式必须是同一个,否则应为外部使用了while循环,就导致了一个死循环

(4)怎样保证子线程数据回来更新UI的时候不打断用户的滑动操作?

在用户进行滑动过程中,当前的runloop运行在UITrackingRunLoopMode,我们对网络请求一般放在子线程中进行的,而子线程返回给主线程的数据呢,我们要抛给主线程,用来更新UI,这个时候我们可以通过把子线程抛会给主线程更新UI的逻辑包装起来,给它提交到主线程的default模式下面,这样的话,抛回来的任务,当前用户正在滑动过程中处于UITrackingRunLoopMode模式下,我们分派到defalut的模式下的任务就不会执行,而当我们手停止滑动操作之后,当前线程它的mode切换到了,default模式下,这个时候处理子线程上抛给主线程的任务,对它进行处理,这样就不会打断用户的滑动操作。

你可能感兴趣的:(RunLoop总结)