iOS面试题:runloop 的 mode 作用是什么?

2024-05-10 17:12

1. iOS面试题:runloop 的 mode 作用是什么?

 在 CoreFoundation 里面关于 RunLoop 有 5 个类,分别对应不同的概念:
   上面的 Source/Timer/Observer 被统称为 mode item,一个 item 可以被同时加入多个 mode。但一个 item 被重复加入同一个 mode 时是不会有效果的。如果一个 mode 中一个 item 都没有,则 RunLoop 会直接退出,不进入循环。
   这些概念的包含关系如下图所示:
                                            线程的运行的过程中需要去处理不同情境的不同事件,mode 则是这个情景的标识,告诉当前应该响应哪些事件。一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个 Mode 被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。 
   CFRunLoopMode 和 CFRunLoop 的结构大致如下:
   这里有个概念叫 CommonModes:一个 Mode 可以将自己标记为 Common 属性(通过将其 ModeName 添加到 RunLoop 的 commonModes 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将  _commonModeItems 里的 Source/Observer/Timer 同步到具有 Common 标记的所有 Mode 里。
   应用场景举例:主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为 Common 属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个 TableView 时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作,因为这个 Timer 作为一个 mode item 并没有被添加到 commonModeItems 里,所以它不会被同步到其他 Common Mode 里。
   有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 commonModeItems 中。commonModeItems 被 RunLoop 自动更新到所有具有 Common 属性的 Mode 里去。
   CFRunLoop 对外暴露的管理 Mode 接口只有下面 2 个:
   Mode 暴露的管理 mode item 的接口有下面几个:
   你只能通过 mode name 来操作内部的 mode,当你传入一个新的 mode name 但 RunLoop 内部没有对应 mode 时,RunLoop会自动帮你创建对应的 CFRunLoopModeRef。对于一个 RunLoop 来说,其内部的 mode 只能增加不能删除。
   苹果公开提供的 Mode 有两个,你可以用这两个 Mode Name 来操作其对应的 Mode:
   同时苹果还提供了一个操作 Common 标记的字符串:kCFRunLoopCommonModes (NSRunLoopCommonModes),你可以用这个字符串来操作 Common Items,或标记一个 Mode 为 Common。使用时注意区分这个字符串和其他 mode name。
    更多: iOS面试题合集  

iOS面试题:runloop 的 mode 作用是什么?

2. ios RunLoop理解

 RunLoop 就是一种循环,只不过它这种循环比较高级。一般的 while 循环会导致 CPU 进入忙等待状态,而 RunLoop 则是一种“闲”等待,当没有事件时,RunLoop 会进入休眠状态,有事件发生时, RunLoop 会去找对应的 Handler 处理事件。RunLoop 可以让线程在需要做事的时候忙起来,不需要的话就让线程休眠,会一直保持不会直接退出。
   RunLoop 实际上是一个对象,这个对象在循环中用来处理程序运行过程中出现的各种事件(比如说触摸事件、UI刷新事件、定时器事件、Selector事件),从而保持程序的持续运行。
   RunLoop有5种模式   NSDefaultRunLoopMode (默认模式,有事件响应的时候,会阻塞旧事件)   NSRunLoopCommonModes (普通模式,不会影响任何事件)   UITrackingRunLoopMode (只能是有事件的时候才会响应的模式,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode影响)
   还有两种系统级别的模式   一个是app刚启动的时候会执行一次   另外一个是系统检测app各种事件的模式
   RunLoop的作用就是用来管理线程的, 当线程的RunLoop开启之后,线程就会在执行完成任务后,进入休眠状态,随时等待接收新的任务,而不是退出。
   RunLoop 和线程是息息相关的,我们知道线程的作用是用来执行特定的一个或多个任务,在默认情况下,线程执行完之后就会退出,就不能再执行任务了。这时我们就需要采用一种方式来让线程能够不断地处理任务,并不退出。所以,我们就有了 RunLoop。
   一条线程对应一个RunLoop对象,每条线程都有唯一一个与之对应的 RunLoop 对象。   RunLoop 并不保证线程安全。我们只能在当前线程内部操作当前线程的 RunLoop 对象,而不能在当前线程内部去操作其他线程的 RunLoop 对象方法。   RunLoop 对象在第一次获取 RunLoop 时创建,销毁则是在线程结束的时候。   主线程的 RunLoop 对象系统自动帮助我们创建好了,而子线程的 RunLoop对象需要我们主动创建和维护。
    默认情况下主线程的 RunLoop是启动的    main.m文件如下所示: