iOS基础知识4

来源:互联网 发布:淘宝申请延长收货时间 编辑:程序博客网 时间:2024/06/05 06:12

一、RunLoop和线程有什么关系?

每个线程,包括主线程(main thread),都有与之对应的RunLoop对象。

主线程的RunLoop是默认启动的,子线程的RunLoop默认是不开启的,需要手动开启子线程的RunLoop。

iOS程序里面,程序启动后会有这样的一个main()函数:

int main(int argc, char * argv[]) {      @autoreleasepool {            return UIApplicationMain(argc, argv, nil,               NSStringFromClass([AppDelegate class]));      }}
这是主循环,保证我们的程序一直运行下去。

UIApplicationMain()函数,这个方法会为main thread设置一个NSRunLoop对象,这就解释了:为什么我们的应用可以在无人操作的时候休息,需要让它干活的时候又能立马响应。
对其它线程来说,RunLoop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。
在任何一个Cocoa程序的线程中,都可以通过以下代码来获取到当前线程的RunLoop。

NSRunLoop *runloop = [NSRunLoop currentRunLoop];
RunLoop在执行完毕后,就会进入休眠,只有在某个情况触发了,RunLoop才会继续被调用。




二、RunLoop的mode作用是什么?

Mode主要是用来指定事件在运行循环中得优先级,有:

        1、NSDefaultRunLoopMode(KCFRunLoopDefaultMode) -> 默认

        2、UITrackingRunLoopMode:ScrollView滑动时会切换到该Mode

        3、UIInitializationRunLoopMode -> Run Loop启动时,会切换到该Mode

        4、NSRunLoopCommonModes(KCFRunLoopCommonModes) -> Mode集合


苹果公开的Mode有两个:

NSDefaultRunLoopMode(KCFRunLoopDefaultMode)和NSRunLoopCommonModes(KCFRunLoopCommonModes)


在编程中如果我们把一个NSTimer对象以NSDefaultRunLoopMode(KCFRunLoopDefaultMode)添加到主运行循环中,ScrollView滚动过程中会因为Mode的切换,而导致NSTimer将不再被调度。当我们滚动的时候,也希望不调度,那就应该使用默认模式。但是,如果我们希望ScrollView在滚动时,计时器也要回调,那就应该使用NSRunLoopCommonModes(KCFRunLoopCommonModes)。




三、以+scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?

RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下来重启成新的mode。ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动。

如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候,ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。

所以:

Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。




四、猜想RunLoop内部是如何实现的?

本质:内部就是 do-while 循环,在这个循环内部不断地处理各种事件(任务),比如:Source、Timer、Observer。
每条线程都有唯一一个RunLoop对象与之对应,主线程的RunLoop默认已经启动 。

子线程的RunLoop需要手动启动。
每次RunLoop启动时,只能指定其中一个mode,这个mode被称作currentMode。

如果需要切换mode,只能退出loop,再重新指定一个mode进入,这样做主要是为了隔离不同mode中的Source、Timer、Observer,让其互不影响。


一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑是这样的:

function loop() {    initialize();    do {        var message = get_next_message();        process_message(message);    } while (message != quit);}
伪代码while循环:

// http://weibo.com/luohanchenyilong/// https://github.com/ChenYilongint main(int argc, char * argv[]) {    //程序一直运行状态    while (AppIsRunning) {        //睡眠状态,等待唤醒事件        id whoWakesMe = SleepForWakingUp();        //得到唤醒事件        id event = GetEvent(whoWakesMe);        //开始处理事件        HandleEvent(event);    }    return 0;}