首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >iOS runloop 的基本使用 、构成及应用案例(基于CFRunLoopDoSources0自定义Operation)

iOS runloop 的基本使用 、构成及应用案例(基于CFRunLoopDoSources0自定义Operation)

作者头像
公众号iOS逆向
发布2021-03-24 15:49:36
发布2021-03-24 15:49:36
1.3K0
举报
文章被收录于专栏:iOS逆向与安全iOS逆向与安全

点击上方蓝字关注我们

引言

  • RunLoop: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

I 、使用例子

1.1 add target/action for particular event

  • 往运行循环添加特定事件的目标和动作(add target/action for particular event)
代码语言:javascript
复制
//--- 使用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];

1.2 往运行循环添加timer

代码语言:javascript
复制
#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];
}

1.3 UI 事件处理的NSRunLoopMode、和定时器的NSRunLoopMode 的关系是什么样的时候,可以保证它们能并发执行不影响个自的运行?

这里写图片描述

  • 答:定时器的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 在使用任何 private mode (比如 _kCFStreamBlockingOpenMode、_kCFStreamSocketReadPrivateMode)时,你所设置的 NSTimer 任务还是会被冷落延后执行。

主线程 Runloop 大部分时候都是以 kCFRunLoopDefaultMode 和 UITrackingRunLoopMode 这两种 mode 运行

II 、Runloop的构成

-每次 loop 只会以一种 mode 运行,以该 mode 运行的时候,就只执行和该 mode 相关的任务,只通知该 mode 注册过的 observer

1、Runloop与Thread是一一绑定的,但是并不是一个Thread只能起一个Runloop,它可以起很多,但是必须是嵌套结构,根Runloop只有一个 2、系统会预定义几个RunloopMode。一个Runloop有多个Mode 3、CFRunloopSource,CFRunloopTimer,CFRunloopObserver这些元素是在Mode里面的,Mode与这些元素的对应关系也是1对多的;

2.1 NSRunloop的实现

在这里插入图片描述

跟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;

2.2 CFRunloopSource

source是RunLoop的数据源(输入源)的抽象类(protocol),Runloop定义了两个Version的Source:

1、Source0:处理App内部事件,App自己负责管理(触发),如UIEvent,CFSocket; 2、Source1:由Runloop和内核管理,mach port驱动,如CFMachPort(轻量级的进程间通信的方式,NSPort就是对它的封装,还有Runloop的睡眠和唤醒就是通过它来做的),CFMessagePort;

  • 具体的有以下事件源

  • input sources (Selector Sources、Port、Customer) 是异步的
  • timer sources 是同步的
  • Port:内核通过port这种方式将信息发送,而mach则监听内核发来的port信息,然后将其整理,打包发给runloop。
  • Customer:开发人员自己发送
  • Selector Sources:NSObject类提供了很多方法供我们使用添加到runloop
  • Timer Sources:它的事件发送是同步的
  • observe不属于事件源,它只是监听runloop本身的状态,并不会影响runloop的生命周期

2.3 RunLoopMode

一个事件循环必须在某种模式下跑,系统会预定义几个模式。一个Runloop有多个Mode;

代码语言:javascript
复制
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

  • kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
  • UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
  • NSTaskDeathCheckMode: Used by NSTask to check if the task is still running

private mode:

  • GSEventReceiveRunLoopMode: Receiving system events
  • FigPlayerBlockingRunLoopMode: QuickTime related
  • com.apple.securityd.runloop :Communication with securityd. Used by SpringBoard only.
  • UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。第一个页面加载之后就切换为NSDefaultRunLoopMode了
  • Various private run loop modes used by CFNetwork for blocking operations_kCFStreamBlockingOpenMode: CFStream 里用来调度网络任务所使用的 private modehttps://opensource.apple.com/source/CF/CF-476.19/CFStream.c.auto.html

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 的封装。

2.3.1 从调用堆栈来看Runloop

  • backtrace : 列出堆栈信息来查找GraphicsServices的身影
代码语言:javascript
复制
(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层的事件分发

2.3.2 runloop模式的切换

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS逆向 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • I 、使用例子
    • 1.1 add target/action for particular event
    • 1.2 往运行循环添加timer
    • 1.3 UI 事件处理的NSRunLoopMode、和定时器的NSRunLoopMode 的关系是什么样的时候,可以保证它们能并发执行不影响个自的运行?
  • II 、Runloop的构成
    • 2.1 NSRunloop的实现
    • 2.2 CFRunloopSource
    • 2.3 RunLoopMode
      • 2.3.1 从调用堆栈来看Runloop
      • 2.3.2 runloop模式的切换
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档