初窥RunLoop

262 阅读2分钟

RunLoop是什么?

顾名思义,在程序运行中循环做事情

应用的场景

  • 定时器(Timer),PerfomeSelector
  • GCD Async Main Queue
  • 事件响应,手势识别,界面刷新
  • 网络请求
  • AutoreleasePool

如果没有RunLoop,程序直接退出:

如果有了RunLoop:

int main(int argc, char * argv[]) {                     
    @autoreleasepool {
     return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
   }
}

访问和使用RunLoop

  • Foundation: NSRunLoop
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop];    // 获得主线程的RunLoop对象
  • Core Foundation: CFRunLoop
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain();    // 获得主线程的RunLoop对象

Core Foundation中关于RunLoop的5个类

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef
typedef struct __CFRunLoop * CFRunLoopRef
struct __CFRunLoop{
    pthread_t _pthread;
    CFMutableSetRef _commodes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
}

typedef struct __CFRunLoopMode * CFRunLoopModeRef
struct __CFRunLoopMode{
    CFStringRef _name;
    CFMutableSetRef _soruce0;
    CFMutableSetRef _source1;
    CFMutableArrayRef _observe;
    CFMutableArrayRef _timers;
}

CFRunLoopMode和RunLoop的运行模式:

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

Source0 : 触摸事件处理 performSelector:onThread:

Source1 : 基于Port的线程间通信 系统事件捕捉

Timers : NSTimer performSelector:withObject:afterDelay:

Observers : 用于监听RunLoop的状态 UI刷新(BeforeWaiting) Autorelease pool(BeforeWaiting)


CFRunLoopObserverRef

    typedef CF_OPTIOns(CFOptionFlags,CFRunLoopActivity){
        KCFRunLoopEntry = (1UL << 0),        //即将进入Loop
        KCFRunLoopBeforeTimes = (1UL << 1),  //即将处理Timer
        KCFRunLoopBeforeSources = (1UL << 2),//即将处理Source
        KCFRunLoopBeforeWating = (1UL << 5), //即将进入休眠
        KCFRunLoopAfterWating = (1UL << 6),  //刚从休眠中唤醒
        KCFRunLoopExit = (1UL << 7),         //即将进退出Loop
        KCFRunLoopAllActivities = 0x0FFFFFFU 
    }

RunLoop的运行逻辑

常见的2种Mode:

1.kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

RunLoop与线程的关系

  • 每条线程都有唯一一个与之对应的RunLoop对象
  • RunLoop保存在一个全局的字典里,key = tread,value = RunLoop
  • 线程刚创建时并没有Runloop对象,RunLoop会在第一次获取时创建
  • RunLoop会在线程销毁时销毁
  • 主线程默认自动开启RunLoop,子线程默认没有开启