天天看点

iOS基础知识4

一、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/ChenYilong
int main(int argc, char * argv[]) {
    //程序一直运行状态
    while (AppIsRunning) {
        //睡眠状态,等待唤醒事件
        id whoWakesMe = SleepForWakingUp();
        //得到唤醒事件
        id event = GetEvent(whoWakesMe);
        //开始处理事件
        HandleEvent(event);
    }
    return 0;
}