iOS进阶回顾五「RunLoop」

  • RunLoop从字面意思上理解就是一个运行循环。
    RunLoop的基本作用
  • 保持程序的持续运行
  • 处理App中的各种事件,比如 :触摸事件、定时器事件等
  • 节省CPU资源,提高效率 比如:该做事做事,该休息休息

可以利用下面的伪代码演示:

int retVal = 0;
  do
{
        //睡眠中等待消息
        int message = sleep_and_wait();
        // 处理消息
        retVal = process_message(message);
        
} while(0==retVal);

iOS中有两种方式访问Runloop

  1. Foundation:NSRunLoop
  2. Core Foundation:CFRunLoopRef
    NSRunLoopCFRunLoopRef都代表着RunLoop对象
  • 获取RunLoop对象
 //获取RunLoop对象的方法
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
    
    NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
    NSLog(@"%p %p",runloop,mainRunLoop);//0x600003ba8000 0x600003ba8000
    NSLog(@"%p %p"runloop2,CFRunLoopGetMain());//0x600002f08100 0x600002f08100
  • 总结
    我们可以看到两个地址是不一样的,但是通过打印输出地址和C语言的地址是一样的,说明NSRunLoop是对CFRunLoopRef的包装
<CFRunLoop 0x600002590000 [0x7fff805ed4e0]>{wakeup port = 0x1d03, stopped = false, ignoreWakeUps = false, 
current mode = kCFRunLoopDefaultMode,
common modes = <CFBasicHash 0x6000017936f0 [0x7fff805ed4e0]>{type = mutable set, count = 2,
entries =>
    0 : <CFString 0x7fff8665f460 [0x7fff805ed4e0]>{contents = "UITrackingRunLoopMode"}
    2 : <CFString 0x7fff806008e0 [0x7fff805ed4e0]>{contents = "kCFRunLoopDefaultMode"}
}
,
common mode items = <CFBasicHash 0x6000017bd680 [0x7fff805ed4e0]>{type = mutable set, count = 12,
entries =>
    0 : <CFRunLoopSource 0x600002c90240 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = PurpleEventSignalCallback (0x7fff3815d9f5)}}
    1 : <CFRunLoopObserver 0x600002894820 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff4715bddc), context = <CFArray 0x6000017cd5f0 [0x7fff805ed4e0]>{type = mutable-small, count = 1, values = (
    0 : <0x7fce34004038>
)}}
    2 : <CFRunLoopObserver 0x6000028946e0 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = _afterCACommitHandler (0x7fff4718bc19), context = <CFRunLoopObserver context 0x7fce33c01930>}
    3 : <CFRunLoopObserver 0x600002894640 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = _beforeCACommitHandler (0x7fff4718bbb0), context = <CFRunLoopObserver context 0x7fce33c01930>}
    4 : <CFRunLoopObserver 0x600002894780 [0x7fff805ed4e0]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff4715bddc), context = <CFArray 0x6000017cd5f0 [0x7fff805ed4e0]>{type = mutable-small, count = 1, values = (
    0 : <0x7fce34004038>
)}}
    9 : <CFRunLoopSource 0x600002c98000 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x3003, callout = PurpleEventCallback (0x7fff3815da01)}}
    10 : <CFRunLoopSource 0x600002c9cfc0 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 41731, subsystem = 0x7fff81eccfc8, context = 0x600003d8c360}}
    12 : <CFRunLoopObserver 0x60000289caa0 [0x7fff805ed4e0]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = _UIGestureRecognizerUpdateObserver (0x7fff46d134c2), context = <CFRunLoopObserver context 0x60000329d490>}
    15 : <CFRunLoopObserver 0x60000289c8c0 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv (0x7fff2afc03ce), context = <CFRunLoopObserver context 0x0>}
    16 : <CFRunLoopSource 0x600002c943c0 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x600002c98180, callout = __handleEventQueue (0x7fff471f5095)}}
    17 : <CFRunLoopSource 0x600002c94300 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -2, context = <CFRunLoopSource context>{version = 0, info = 0x6000017be4f0, callout = __handleHIDEventFetcherDrain (0x7fff471f50a1)}}
    18 : <CFRunLoopSource 0x600002c94240 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x600003d8c540, callout = FBSSerialQueueRunLoopSourceHandler (0x7fff36330576)}}
}
,
modes = <CFBasicHash 0x6000017937b0 [0x7fff805ed4e0]>{type = mutable set, count = 4,
entries =>
    2 : <CFRunLoopMode 0x600002298340 [0x7fff805ed4e0]>{name = UITrackingRunLoopMode, port set = 0x5203, queue = 0x60000379c980, source = 0x60000379ca80 (not fired), timer port = 0x2d03, 
    sources0 = <CFBasicHash 0x6000017bd6e0 [0x7fff805ed4e0]>{type = mutable set, count = 4,
entries =>
    0 : <CFRunLoopSource 0x600002c90240 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = PurpleEventSignalCallback (0x7fff3815d9f5)}}
    1 : <CFRunLoopSource 0x600002c943c0 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x600002c98180, callout = __handleEventQueue (0x7fff471f5095)}}
    2 : <CFRunLoopSource 0x600002c94240 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x600003d8c540, callout = FBSSerialQueueRunLoopSourceHandler (0x7fff36330576)}}
    3 : <CFRunLoopSource 0x600002c94300 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -2, context = <CFRunLoopSource context>{version = 0, info = 0x6000017be4f0, callout = __handleHIDEventFetcherDrain (0x7fff471f50a1)}}
}
,
    sources1 = <CFBasicHash 0x6000017bd710 [0x7fff805ed4e0]>{type = mutable set, count = 2,
entries =>
    0 : <CFRunLoopSource 0x600002c98000 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x3003, callout = PurpleEventCallback (0x7fff3815da01)}}
    1 : <CFRunLoopSource 0x600002c9cfc0 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 41731, subsystem = 0x7fff81eccfc8, context = 0x600003d8c360}}
}
,
    observers = (
    "<CFRunLoopObserver 0x600002894780 [0x7fff805ed4e0]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff4715bddc), context = <CFArray 0x6000017cd5f0 [0x7fff805ed4e0]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fce34004038>\n)}}",
    "<CFRunLoopObserver 0x60000289caa0 [0x7fff805ed4e0]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = _UIGestureRecognizerUpdateObserver (0x7fff46d134c2), context = <CFRunLoopObserver context 0x60000329d490>}",
    "<CFRunLoopObserver 0x600002894640 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = _beforeCACommitHandler (0x7fff4718bbb0), context = <CFRunLoopObserver context 0x7fce33c01930>}",
    "<CFRunLoopObserver 0x60000289c8c0 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv (0x7fff2afc03ce), context = <CFRunLoopObserver context 0x0>}",
    "<CFRunLoopObserver 0x6000028946e0 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = _afterCACommitHandler (0x7fff4718bc19), context = <CFRunLoopObserver context 0x7fce33c01930>}",
    "<CFRunLoopObserver 0x600002894820 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff4715bddc), context = <CFArray 0x6000017cd5f0 [0x7fff805ed4e0]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fce34004038>\n)}}"
),
    timers = <CFArray 0x600003d88540 [0x7fff805ed4e0]>{type = mutable-small, count = 0, values = ()},
    currently 592493202 (132206778385536) / soft deadline in: 1.84466119e+10 sec (@ -1) / hard deadline in: 1.84466119e+10 sec (@ -1)
},

    3 : <CFRunLoopMode 0x600002298410 [0x7fff805ed4e0]>{name = GSEventReceiveRunLoopMode, port set = 0x2e03, queue = 0x60000379cb00, source = 0x60000379cc00 (not fired), timer port = 0x4f03, 
    sources0 = <CFBasicHash 0x6000017bd830 [0x7fff805ed4e0]>{type = mutable set, count = 1,
entries =>
    0 : <CFRunLoopSource 0x600002c90240 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = PurpleEventSignalCallback (0x7fff3815d9f5)}}
}
,
    sources1 = <CFBasicHash 0x6000017bd650 [0x7fff805ed4e0]>{type = mutable set, count = 1,
entries =>
    0 : <CFRunLoopSource 0x600002c98240 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x3003, callout = PurpleEventCallback (0x7fff3815da01)}}
}
,
    observers = (null),
    timers = (null),
    currently 592493202 (132206780901271) / soft deadline in: 1.84466119e+10 sec (@ -1) / hard deadline in: 1.84466119e+10 sec (@ -1)
},

    4 : <CFRunLoopMode 0x600002290340 [0x7fff805ed4e0]>{name = kCFRunLoopDefaultMode, port set = 0x1e03, queue = 0x600003790c80, source = 0x600003790d80 (not fired), timer port = 0x2303, 
    sources0 = <CFBasicHash 0x6000017bd740 [0x7fff805ed4e0]>{type = mutable set, count = 4,
entries =>
    0 : <CFRunLoopSource 0x600002c90240 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = PurpleEventSignalCallback (0x7fff3815d9f5)}}
    1 : <CFRunLoopSource 0x600002c943c0 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x600002c98180, callout = __handleEventQueue (0x7fff471f5095)}}
    2 : <CFRunLoopSource 0x600002c94240 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x600003d8c540, callout = FBSSerialQueueRunLoopSourceHandler (0x7fff36330576)}}
    3 : <CFRunLoopSource 0x600002c94300 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -2, context = <CFRunLoopSource context>{version = 0, info = 0x6000017be4f0, callout = __handleHIDEventFetcherDrain (0x7fff471f50a1)}}
}
,
    sources1 = <CFBasicHash 0x6000017bd800 [0x7fff805ed4e0]>{type = mutable set, count = 2,
entries =>
    0 : <CFRunLoopSource 0x600002c98000 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x3003, callout = PurpleEventCallback (0x7fff3815da01)}}
    1 : <CFRunLoopSource 0x600002c9cfc0 [0x7fff805ed4e0]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 41731, subsystem = 0x7fff81eccfc8, context = 0x600003d8c360}}
}
,
    observers = (
    "<CFRunLoopObserver 0x600002894780 [0x7fff805ed4e0]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff4715bddc), context = <CFArray 0x6000017cd5f0 [0x7fff805ed4e0]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fce34004038>\n)}}",
    "<CFRunLoopObserver 0x60000289caa0 [0x7fff805ed4e0]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = _UIGestureRecognizerUpdateObserver (0x7fff46d134c2), context = <CFRunLoopObserver context 0x60000329d490>}",
    "<CFRunLoopObserver 0x600002894640 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = _beforeCACommitHandler (0x7fff4718bbb0), context = <CFRunLoopObserver context 0x7fce33c01930>}",
    "<CFRunLoopObserver 0x60000289c8c0 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv (0x7fff2afc03ce), context = <CFRunLoopObserver context 0x0>}",
    "<CFRunLoopObserver 0x6000028946e0 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = _afterCACommitHandler (0x7fff4718bc19), context = <CFRunLoopObserver context 0x7fce33c01930>}",
    "<CFRunLoopObserver 0x600002894820 [0x7fff805ed4e0]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff4715bddc), context = <CFArray 0x6000017cd5f0 [0x7fff805ed4e0]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fce34004038>\n)}}"
),
    timers = <CFArray 0x600003d9e520 [0x7fff805ed4e0]>{type = mutable-small, count = 0, values = ()},
    currently 592493202 (132206780976526) / soft deadline in: 1.84466119e+10 sec (@ -1) / hard deadline in: 1.84466119e+10 sec (@ -1)
},

    5 : <CFRunLoopMode 0x60000229ee50 [0x7fff805ed4e0]>{name = kCFRunLoopCommonModes, port set = 0xa603, queue = 0x600003799580, source = 0x600003799600 (not fired), timer port = 0xa403, 
    sources0 = (null),
    sources1 = (null),
    observers = (null),
    timers = (null),
    currently 592493202 (132206782855209) / soft deadline in: 1.84466119e+10 sec (@ -1) / hard deadline in: 1.84466119e+10 sec (@ -1)
},

}
}
  • RunLoop与线程的关系
    1. 每个线程都有唯一的一个与之对应的RunLoop对象
    2. RunLoop保存在一个全局的Dictionary里,线程作为Key,Runloop作为value
    3.线程刚创建的时候并没有RunLoop对象,RunLoop会在第一次获取它时创建
    4.RunLoop会在线程结束时销毁
    5.主线程的RunLoop已经自动获取(创建)子线程默认没有开启RunLoop
  • 与RunLoop相关的类
  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef
  • 我们可以通查看RunLoop源码看到RunLoop的底层结构[如下所示]
    RunLoop的底层结构体内容

    我们可以看到上图RunLoop内部含有pthread线程以及Set集合的_modes说明一个RunLoop中有多种模式,但是只有一个为当前模式查看CFRunLoopMode如下
    模式包含的内容

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会立马退出

  • CFRunLoopModeRef常见的两种Mode

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

  • 下面研究Mode的内容:
    1. Source0:

触摸事件处理
performSelector:onThread

我们利用touchesBegan方法打断点查看函数调用栈信息;

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"%s",__func__);
}
  • 在控制台输入bt(breakpoint trace)
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x000000010b5be75d RunLoop`-[RunLoopBasicVC touchesBegan:withEvent:](self=0x00007fb6ea4172b0, _cmd="touchesBegan:withEvent:", touches=1 element, event=0x00006000000dc6e0) at RunLoopBasicVC.m:33:5
    frame #1: 0x00007fff4718f0bf UIKitCore`forwardTouchMethod + 340
    frame #2: 0x00007fff4718ef5a UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49
    frame #3: 0x00007fff4719df3e UIKitCore`-[UIWindow _sendTouchesForEvent:] + 1867
    frame #4: 0x00007fff4719fb26 UIKitCore`-[UIWindow sendEvent:] + 4596
    frame #5: 0x00007fff4717b1a7 UIKitCore`-[UIApplication sendEvent:] + 356
    frame #6: 0x00007fff471faa18 UIKitCore`__dispatchPreprocessedEventFromEventQueue + 6847
    frame #7: 0x00007fff471fd4de UIKitCore`__handleEventQueueInternal + 5980
    frame #8: 0x00007fff23afbac1 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #9: 0x00007fff23afb9ec CoreFoundation`__CFRunLoopDoSource0 + 76
    frame #10: 0x00007fff23afb1c4 CoreFoundation`__CFRunLoopDoSources0 + 180
    frame #11: 0x00007fff23af5ecf CoreFoundation`__CFRunLoopRun + 1263
    frame #12: 0x00007fff23af56b6 CoreFoundation`CFRunLoopRunSpecific + 438
    frame #13: 0x00007fff3815cbb0 GraphicsServices`GSEventRunModal + 65
  * frame #14: 0x00007fff47162a67 UIKitCore`UIApplicationMain + 1621
    frame #15: 0x000000010b5bea24 RunLoop`main(argc=1, argv=0x00007ffee4640cf8) at main.m:18:12
    frame #16: 0x00007fff5123bcf5 libdyld.dylib`start + 1
    frame #17: 0x00007fff5123bcf5 libdyld.dylib`start + 1
(lldb) 

我们看到调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__看到Source0处理触摸事件,performSelector:onThread也是由Source0处理

2.Source1

基于Port的线程间通信
系统事件的捕捉(也就是说通过Source1捕捉,然后分发到Source0里面处理的)

3.Timers

NSTimer
performSelector:withObject:afterDelay:

4.Observers

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

  • CFRunLoopObserverRef的状态
    RunLoop的状态

    -我们自己添加RunLoop的状态监听来查看它是如何工作的
// 观察方法
void observeRunLoopActivites(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"kCFRunLoopEntry");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"kCFRunLoopBeforeTimers");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"kCFRunLoopBeforeSources");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"kCFRunLoopBeforeWaiting");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"kCFRunLoopAfterWaiting");
            break;
        case kCFRunLoopExit:
            NSLog(@"kCFRunLoopExit");
            break;
        default:
            break;
    }
}

//--------------
 //创建Observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate( kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, observeRunLoopActivites, NULL);
    
    //添加监听者到RunLoop中 通用模式(包含kCFRunLoopDefaultMode和 UITrackingRunLoopMode)
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    //释放observer
    CFRelease(observer);
    
    //创建方式二
    CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        //直接使用block处理
    });
  • 再次调用touchesBegan方法
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"%s",__func__);
}

得到如下的输出:

2019-10-11 22:22:33.054255+0800 RunLoop[93336:1305548] kCFRunLoopAfterWaiting
2019-10-11 22:22:33.054488+0800 RunLoop[93336:1305548] kCFRunLoopBeforeTimers
2019-10-11 22:22:33.054584+0800 RunLoop[93336:1305548] kCFRunLoopBeforeSources
2019-10-11 22:22:33.056189+0800 RunLoop[93336:1305548] -[RunLoopBasicVC touchesBegan:withEvent:]
2019-10-11 22:22:33.056511+0800 RunLoop[93336:1305548] kCFRunLoopBeforeTimers
2019-10-11 22:22:33.056858+0800 RunLoop[93336:1305548] kCFRunLoopBeforeSources
2019-10-11 22:22:33.058080+0800 RunLoop[93336:1305548] kCFRunLoopBeforeTimers
2019-10-11 22:22:33.058711+0800 RunLoop[93336:1305548] kCFRunLoopBeforeSources
2019-10-11 22:22:33.060141+0800 RunLoop[93336:1305548] kCFRunLoopBeforeTimers
2019-10-11 22:22:33.061535+0800 RunLoop[93336:1305548] kCFRunLoopBeforeSources
2019-10-11 22:22:33.063411+0800 RunLoop[93336:1305548] kCFRunLoopBeforeTimers
2019-10-11 22:22:33.063973+0800 RunLoop[93336:1305548] kCFRunLoopBeforeSources
2019-10-11 22:22:33.064541+0800 RunLoop[93336:1305548] kCFRunLoopBeforeWaiting
2019-10-11 22:22:35.063183+0800 RunLoop[93336:1305548] kCFRunLoopAfterWaiting
2019-10-11 22:22:35.063707+0800 RunLoop[93336:1305548] kCFRunLoopBeforeTimers
2019-10-11 22:22:35.065788+0800 RunLoop[93336:1305548] kCFRunLoopBeforeSources
2019-10-11 22:22:35.066229+0800 RunLoop[93336:1305548] kCFRunLoopBeforeWaiting
2019-10-11 22:22:35.066682+0800 RunLoop[93336:1305548] kCFRunLoopAfterWaiting
2019-10-11 22:22:35.067281+0800 RunLoop[93336:1305548] kCFRunLoopBeforeTimers
2019-10-11 22:22:35.067672+0800 RunLoop[93336:1305548] kCFRunLoopBeforeSources
2019-10-11 22:22:35.067815+0800 RunLoop[93336:1305548] kCFRunLoopBeforeWaiting

我们可以看到确实是Sources状态下处理touchesBegan方法
再次编写定时器代码

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//    NSLog(@"%s",__func__);
    [NSTimer scheduledTimerWithTimeInterval:3 repeats:NO block:^(NSTimer * _Nonnull timer) {
        NSLog(@"随风流年--定时器");
    }];
}
 22:30:23.829237+0800 RunLoop[93381:1310752] kCFRunLoopBeforeWaiting
 22:30:26.026510+0800 RunLoop[93381:1310752] kCFRunLoopAfterWaiting
 22:30:26.026815+0800 RunLoop[93381:1310752] kCFRunLoopBeforeTimers
 22:30:26.027012+0800 RunLoop[93381:1310752] kCFRunLoopBeforeSources
 22:30:26.027206+0800 RunLoop[93381:1310752] kCFRunLoopBeforeWaiting
 22:30:26.027431+0800 RunLoop[93381:1310752] kCFRunLoopAfterWaiting
 22:30:26.027608+0800 RunLoop[93381:1310752] kCFRunLoopBeforeTimers
 22:30:26.027763+0800 RunLoop[93381:1310752] kCFRunLoopBeforeSources
 22:30:26.027924+0800 RunLoop[93381:1310752] kCFRunLoopBeforeWaiting
 22:30:26.796064+0800 RunLoop[93381:1310752] kCFRunLoopAfterWaiting
 22:30:26.796507+0800 RunLoop[93381:1310752] 随风流年--定时器
 22:30:26.796797+0800 RunLoop[93381:1310752] kCFRunLoopBeforeTimers
 22:30:26.797004+0800 RunLoop[93381:1310752] kCFRunLoopBeforeSources
 22:30:26.797196+0800 RunLoop[93381:1310752] kCFRunLoopBeforeWaiting
  • 模式的切换
 //创建方式二
    CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        //直接使用block处理
        
        switch (activity) {
            case kCFRunLoopEntry:
            {
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());  NSLog(@"kCFRunLoopEntry--%@",mode);
                CFRelease(mode);
                break;
            }
            case kCFRunLoopExit:{
                CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent()); NSLog(@"kCFRunLoopExit --%@",mode);
                CFRelease(mode);
                break;
            }
            default:
                break;
        }
    });

如果我们滚动表单时会打印输出kCFRunLoopDefaultModeUITrackingRunLoopMode的来回切换

RunLoop在实际开发中的应用

  • 控制线程的声明周期
  • 解决NSTimer在滑动时停止工作的问题
  • 监控应用卡顿
  • 性能优化
  • 如果我们编写定时器代码如下:
   static int count = 0;
    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"count=%d----%@",++count,CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent()));
    }];

在滚动列表的时候就定时器的Mode就会切换,这样定时器(timer)就不会继续执行了,那么该怎么办?
可以自己定义Mode下的timer操作

    static int count = 0;

 //自己添加模式
    NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"count=%d----%@",++count,CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent()));
    }];
    [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

15:00:52.325695+0800 RunLoop[982:53172] count=1----kCFRunLoopDefaultMode
15:00:53.325723+0800 RunLoop[982:53172] count=2----kCFRunLoopDefaultMode
15:00:54.325496+0800 RunLoop[982:53172] count=3----kCFRunLoopDefaultMode

但是这样写同样有问题?那就是定时器Timer停不下来了,怎么办?
如何让线程停下来?需要使用的时候开启?不需要使用的时候,停下来呢?
接下来继续 go on

//
//  SFPermenantThread.h
//  NStimer失效
//
//  Created by 随风流年 on 2019/9/6.
//  Copyright © 2019 随风流年. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef void (^SFPermenantThreadTask)(void);

NS_ASSUME_NONNULL_BEGIN

@interface SFPermenantThread : NSObject
/*
 开启一个线程
 */
-(void)run;
//执行一个任务
//-(void)executeTaskWithTarget:(id)target action:(SEL)action object:(id)object;

-(void)executeTaskWithBlock:(SFPermenantThreadTask)task;
/*
 结束一个线程
 */
-(void)stop;


@end

NS_ASSUME_NONNULL_END

.m 的实现文件如下:

//
//  SFPermenantThread.m
//  NStimer失效
//
//  Created by 随风流年 on 2019/9/6.
//  Copyright © 2019 随风流年. All rights reserved.
//

#import "SFPermenantThread.h"

@interface SFThread : NSThread
@end

@implementation SFThread
-(void)dealloc{
    NSLog(@"%s",__func__);
}
@end

/** SFPermenantThread **/

@interface SFPermenantThread()
@property (strong,nonatomic) SFThread *innerThread;
@property (nonatomic, assign, getter=isStopped) BOOL stopped;

@end

@implementation SFPermenantThread
- (instancetype)init{
    if (self = [super init]) {
        self.stopped = NO;
        __weak typeof(self) weakSelf = self;
        self.innerThread = [[SFThread alloc]initWithBlock:^{
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
            while (weakSelf && !weakSelf.stopped) {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
        }];
        
    }
    return self;
}
-(void)run{
    if (!self.innerThread) {
        return;
    }
    [self.innerThread start];
}
-(void)executeTaskWithBlock:(SFPermenantThreadTask)task{
    if (!self.innerThread || !task) return;

    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

-(void)stop{
    if (!self.innerThread) return;
    [self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}
#pragma mark -- private method ---
-(void)__stop
{
    self.stopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.innerThread = nil;
}
-(void)__executeTask:(SFPermenantThreadTask)task{
    task();
}
@end

推荐阅读更多精彩内容