


点击上方蓝字关注我们

An event-processing loop, during which events are received and dispatched to appropriate handlers.1、决定程序在何时应该处理哪些事件:app会有很多事件,Runloop会有一定的机制来管理事件的处理时机 2、调用解耦(Message Queue)
原文链接:https://blog.csdn.net/z929118967/article/details/114638658
//--- 使用UIControl 的addTarget:action:forControlEvents:方法
//将监听方法click注册到“运行循环”,当触发ControlEvent事件时,由“运行循环”通知Target(ViewController) 执行action(@selector)
//Adds a target and action for a particular event (or events) to an internal dispatch table.将特定事件的执行目标和行动添加到内部调度表
[btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
#pragma mark - 计时器的播放实现
- (IBAction)start{
NSLog(@"%s",__FUNCTION__);
//间隔一秒更新counterLabel的显示
//计时器
//往运行循环添加timer的方式一:-------------------------------------------
/**
Creates and returns a new NSTimer object and schedules it on the current run loop in the default mode.
参数说明
1》seconds: double 时间间隔
2》target: The object to which to send the message specified by aSelector when the timer fires. 监听时钟触发的对象
3》Selector: The message to send to target when the timer fires.调用的方法
The selector should have the following signature: timerFireMethod:
- (void)timerFireMethod:(NSTimer *)timer
4》userInfo: The user info for the timer.通常为nil,可用于区分计时器
repeats:if YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.是否重复
*/
// self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES];
//将timer添加到运行循环的方式二-------------------------------------------
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES];
//Registers a given timer with a given input mode.NSRunLoopCommonModes (监听滚动模式)
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode: NSRunLoopCommonModes];
}

这里写图片描述
定时器的model类型设置为NSRunLoopCommonModes事件源,都是处于特定的模式下的,如果和当前runloop的模式不一致则不会得到响应;创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。
因此想让Timer重复的得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。来保证主线程不管怎么切换model,timer都可以得到回调。
或者更简单的解决方式是加入NSRunLoopCommonModes。
主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。 这两个 Mode 都已经被标记为”Common”属性;这样timer会被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去`
主线程 Runloop 大部分时候都是以 kCFRunLoopDefaultMode 和 UITrackingRunLoopMode 这两种 mode 运行
-每次 loop 只会以一种 mode 运行,以该 mode 运行的时候,就只执行和该 mode 相关的任务,只通知该 mode 注册过的 observer
1、Runloop与Thread是一一绑定的,但是并不是一个Thread只能起一个Runloop,它可以起很多,但是必须是嵌套结构,根Runloop只有一个 2、系统会预定义几个RunloopMode。一个Runloop有多个Mode 3、CFRunloopSource,CFRunloopTimer,CFRunloopObserver这些元素是在Mode里面的,Mode与这些元素的对应关系也是1对多的;

在这里插入图片描述
跟Runloop有关的一些模块
1)NSTimer计时器; 2)UIEvent事件; 3)Autorelease机制; 4)NSObject(NSDelayedPerforming): 比如这些方法:
performSelector:withObject:afterDelay:,performSelector:withObject:afterDelay:inModes:,cancelPreviousPerformRequestsWithTarget:selector:object:5)NSObject(NSThreadPerformAddition): 比如这些方法:performSelectorInBackground:withObject:,performSelectorOnMainThread:withObject:waitUntilDone:,performSelector:onThread:withObject:waitUntilDone:6)、Core Animation层的一些东西:CADisplayLink,CATransition,CAAnimation等; 7)、dispatch_get_main_queue(); 8)、NSURLConnection;
source是RunLoop的数据源(输入源)的抽象类(protocol),Runloop定义了两个Version的Source:
1、Source0:
处理App内部事件,App自己负责管理(触发),如UIEvent,CFSocket;2、Source1:由Runloop和内核管理,mach port驱动,如CFMachPort(轻量级的进程间通信的方式,NSPort就是对它的封装,还有Runloop的睡眠和唤醒就是通过它来做的),CFMessagePort;
一个事件循环必须在某种模式下跑,系统会预定义几个模式。一个Runloop有多个Mode;
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
CFMutableSetRef _sources0;//公开的 API 可供开发者调用
CFMutableSetRef _sources1;//只能供系统使用
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;//
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
mach_port_t _timerPort;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
HANDLE _timerPort;
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
};
mode 的结构定义里并无 mainQueue 相关的信息, 所以mainQueue 任务的执行和 mode 无关
1、系统为我们提供了多种模式, 分为两类common mode 和 private mode。
common mode:NSRunLoopCommonModes:包含了多种模式:kCFRunLoopDefaultMode 和UITrackingRunLoopMode、NSTaskDeathCheckMode
private mode:
Mode | Purpose | Part of common modes? |
|---|---|---|
kCFRunLoopDefaultMode | The default run loop mode, almost encompasses every sources. You should always add sources and timers to this mode if there's no special reasons. Can be accessed with the symbol kCFRunLoopDefaultMode and NSDefaultRunLoopMode. | Yes |
NSTaskDeathCheckMode | Used by NSTask to check if the task is still running. | Yes |
_kCFHostBlockingMode _kCFNetServiceMonitorBlockingMode _kCFNetServiceBrowserBlockingMode _kCFNetServiceBlockingMode _kCFStreamSocketReadPrivateMode _kCFStreamSocketCanReadPrivateMode _kCFStreamSocketWritePrivateMode _kCFStreamSocketCanWritePrivateMode _kCFStreamSocketSecurityClosePrivateMode _kCFStreamSocketBogusPrivateMode _kCFURLConnectionPrivateRunLoopMode _kProxySupportLoadingPacPrivateMode _kProxySupportSyncPACExecutionRunLoopMode _kCFStreamSocketSecurityClosePrivateMode | Various private run loop modes used by CFNetwork for blocking operations | No |
UITrackingRunLoopMode | UI tracking. | Yes |
GSEventReceiveRunLoopMode | Receiving system events. | No |
com.apple.securityd.runloop | Communication with securityd. Used by SpringBoard only. | No |
FigPlayerBlockingRunLoopMode | QuickTime related. | No |
2、kCFRunLoopDefaultMode 和 UITrackingRunLoopMode 是属于 common mode
kCFRunLoopDefaultMode 是默认模式下 runloop 使用的 mode,scrollView 滑动的时候切换到 UITrackingRunLoopMode
3、系统还定义了一些 private mode,比如 UIInitializationRunLoopMode 和 GSEventReceiveRunLoopMode。
app 启动 通过`CF_EXPORT CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl); 即可打印这两个 mode
GSEventReceiveRunLoopMode 是属于 GraphicsServices 这个并不公开的 framework
1、GSEvent 封装了系统传递给 app 的重要事件(比如音量按钮事件、屏幕点击事件) 2、UIEvent 也是 GSEvent 的封装。
(lldb) thread backtrace
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x00000001069a337c libweixinDylib.dylib`-[KNSocketManagerTool sentgroupInfo](self=0x000000010b45cb90, _cmd="sentgroupInfo") at KNSocketManagerTool.m:187
frame #1: 0x00000001069a2f28 libweixinDylib.dylib`__44-[KNSocketManagerTool setupfirstconnecTimer]_block_invoke_2((null)=<unavailable>) at KNSocketManagerTool.m:161
frame #2: 0x0000000106a09218 libdispatch.dylib`_dispatch_client_callout + 16
frame #3: 0x0000000106a15334 libdispatch.dylib`_dispatch_continuation_pop + 708
frame #4: 0x0000000106a23f94 libdispatch.dylib`_dispatch_source_latch_and_call + 204
frame #5: 0x0000000106a0b300 libdispatch.dylib`_dispatch_source_invoke + 836
frame #6: 0x0000000106a0e05c libdispatch.dylib`_dispatch_main_queue_callback_4CF + 652
frame #7: 0x000000018bc8a810 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
frame #8: 0x000000018bc883fc CoreFoundation`__CFRunLoopRun + 1660
frame #9: 0x000000018bbb62b8 CoreFoundation`CFRunLoopRunSpecific + 444
frame #10: 0x000000018d66a198 GraphicsServices`GSEventRunModal + 180
frame #11: 0x0000000191bfd7fc UIKit`-[UIApplication _run] + 684
frame #12: 0x0000000191bf8534 UIKit`UIApplicationMain + 208
frame #13: 0x0000000100239550 WeChat`___lldb_unnamed_symbol4942$$WeChat + 608
frame #14: 0x000000018ab995b8 libdyld.dylib`start + 4
dyld->main>UIApplicationMain->GSEventRunModal(Graphics Services处理硬件输入,比如点击,所有的UI事件都是它发出来的)->Runloop->事件队列处理以及UI层的事件分发