iOS-RunLoop

jopen 5年前

RunLoop总结

需要思考问题

为什么程序在使用的时候可以接受用户的触摸事件,不使用的时候什么事件都不发生?

查看一下 main.m 文件,这是程序启动的入口函数。

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

代码调用的时候,主线程也开始运行。文档上说 UIApplicationMain 函数指定了appliacaion的delegate,并且开启了event cycle。那什么是event cycle?

RunLoop

程序的event cycle其实就是RunLoop,RunLoop在程序启动的时候就开启,接受用户的处理事件。

这是官方文档上面的一个图,可以看到RunLoop在线程中循环监听需要处理的事件。能够接收两种不同的事件源

  • Input sources(输入源):传递异步事件。
  • Timer sources(定时器): 传递同步事件、发生在特定时间或者重复的时间间隔。

RunLoop Mode

  • Default模式:几乎包含了所有输入源,一般情况下使用这个模式

    • NSDefaultRunLoopMode (Cocoa)
    • kCFRunLoopDefaultMode (Core Foundation)
    </li>
  • Connection模式

    • 处理NSConnection对象相关事件,系统内部使用,用户基本不会使用。
    • </ul> </li>
    • Modal模式
      • 处理modal panels事件
      • </ul> </li>
      • Event tracking模式:滑动Scrollview、Tableview的时候就处于这个模式

        • UITrackingRunLoopMode(iOS)
        • NSEventTrackingRunLoopMode(cocoa)
        • </ul> </li>
        • Common模式: 模式的组合,所有的模式下都可以处理

          • NSRunLoopCommonModes (Cocoa)
          • kCFRunLoopCommonModes (Core Foundation)
          • </ul> </li> </ul>

            RunLoop与线程的关系

            每条线程都有唯一的RunLoop对象与之对应,主线程的RunLoop是自动创建并启动的。子线程的RunLoop需要手动创建,并且调用 run 方法启动。 currentRunLoop 是延迟加载的,只创建一次.

            [[NSRunLoop currentRunLoop] run];
            </div>

            指定RunLoop运行的模式和过期时间

            - (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
            </div>

            使用场景

            1、NSTimer

            如果在ScrollView里面使用到NSTimer的,需要调用 addTimer:forMode 方法指定model,才能在滚动的时候生效。

            NSTimer创建的定时器因为需要指定model运行,在RunLoop中需要处理各种事件,导致NSTimer不精确。可以使用GCD方法来创建,GCD不受RunLoop的Mode影响.

             dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);          dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);      // 要搞一个强引用,不然定时器创建出来就挂了      self.timer = timer;        // 延迟3秒开始执行      dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));      // 每隔2秒执行一次      uint64_t interval = (uint64_t)(2.0 * NSEC_PER_SEC);      dispatch_source_set_timer(self.timer, start, interval, 0);        // 要执行的任务      dispatch_source_set_event_handler(self.timer, ^{          NSLog(@"%s",__func__);      });        // 启动定时器      dispatch_resume(self.timer);            // 停止定时器  dispatch_cancel(self.timer);
            </div>

            2、ImageView的加载

            图片的加载放在 NSDefaultRunLoopMode ,因为ScrollView的滚动是在 UITrackingRunLoopMode 下运行的。

            3、PerformSelector指定执行的模式

            - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSString *> *)modes;
            </div>

            参考

            1、 官方文档

            2、 http://www.cnblogs.com/xuebao/p/4643704.html

            </div>

            来自: http://www.liuchendi.com/2016/01/04/iOS/28_Runloop/