iOS中的RunLoop

面试题

1.讲讲 RunLoop,项目中有用到吗?

应用1:创建常驻线程

+ (void)networkRequestThreadEntryPoint:(id)__unused object {

    @autoreleasepool {

            [[NSThread currentThread] setName:@"AFNetworking"];

            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

            [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];

            [runLoop run];

            }

    }

+ (NSThread *)networkRequestThread {

    static NSThread *_networkRequestThread = nil;

    static dispatch_once_t oncePredicate;

    dispatch_once(&oncePredicate, ^{

            _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];

            [_networkRequestThread start];

            });

    return _networkRequestThread;

}

应用2:优化定时器NSTimer

我们都知道,可滑动视图在滑动的时候,runloop会切换到UITrackingRunLoopMode,这时NSDefaultRunLoopMode下的NSTimer是不会跑的,在实际运用中,这可能会是一个问题。

NSTimer*timer=[NSTimer timerWithTimeInterval:1.0target:selfselector:@selector(timerTask)userInfo:nil repeats:YES];

//使用NSRunLoopCommonModes,可保证在UITrackingRunLoopMode和NSDefaultRunLoopMode下界面滑动时timer依然在运行

[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

[timer fire];

2.RunLoop和线程间的关系?

1.每条线程都有唯一的一个与之对应的RunLoop对象

2.RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value

3.主线程的RunLoop已经自动创建好了,通过[NSRunloop mainRunLoop];获取主线程的runloop。子线程的RunLoop需要主动创建[NSRunloop currentRunLoop];方法[NSRunLoop currentRunLoop]调用时,会先看一下字典里有没有子线程对应的RunLoop,如果有则直接返回,如果没有则会创建一个,并与子线程一起存入字典

4.RunLoop在第一次获取时创建,在线程结束时销毁

一、什么是RunLoop?

RunLoop就是运行循环, 它在程序运行过程中交替循环做一些事情,如果没有RunLoop,程序执行完毕就会立即退出,如果有RunLoop,程序会一直运行,并且随时响应用户的操作。在没有用户操作的时候就睡觉,充分节省CPU资源,提高程序性能。

二、RunLoop有什么用?

1.保持程序持续运行,iOSApp一启动就会开一个主线程,主线程会开启RunLoop,保证主线程不会被销毁,也就保持了程序持续运行(命令行项目没有开启RunLoop,所以程序执行完就退出了)

2.处理App中各种事件,如触摸事件,定时器事件,Selector事件,网络请求, 线程间的通信,界面刷新,AutoreleasePool释放对象等。

3.节省CPU资源,提高程序性能,iOSApp启动后,当没有事情要做的时候,RunLoop就会睡觉,节省CPU资源。等到有事要做的时候,就去做事。

三、RunLoop对象

iOS中有2套API来访问和使用RunLoop

Core Foundation : CFRunLoopRef

它是开源的: https://opensource.apple.com/tarballs/CF/

Foundation : NSRunLoop (基于CFRunLoopRef的OC封装)

NSRunLoop和CFRunLoopRef都代表着RunLoop对象

获取RunLoop对象

Foundation:[NSRunLoop currentRunLoop];//获取当前线程的RunLoop对象 [NSRunLoop mainRunLoop];//获取主线程的RunLoop对象Core FoundationCFRunLoopGetCurrent();//获取当前线程的RunLoop对象CFRunLoopGetMain();//获取主线程的RunLoop对象

四、RunLoop相关的类

Core Foundation中关于RunLoop的5个类

CFRunLoopRef

CFRunLoopModeRef

CFRunLoopSourceRef

CFRunLoopTimerRef

CFRunLoopObserverRef

我们可以在开源的CFRunLoopRef看到它们之间的关系,源代码里面不止这些, 为了方便这里只拿了部分比较有用的.

我们可以看到CFRunLoopRef里面有CFRunLoopModeRef

// CFRunLoopRef里面有CFRunLoopModeRef

typedef struct __CFRunLoop * CFRunLoopRef;

struct __CFRunLoop {

    pthread_t _pthread;

    CFMutableSetRef _commonModes;

    CFMutableSetRef _commonModeItems;

    CFRunLoopModeRef _currentMode;

    CFMutableSetRef _modes;

};

CFRunLoopModeRef里有CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObserverRef

typedefstruct__CFRunLoopMode*CFRunLoopModeRef;

struct__CFRunLoopMode{

    CFStringRef _name;

    CFMutableSetRef _sources0;

    CFMutableSetRef _sources1;

    CFMutableArrayRef _observers;

    CFMutableArrayRef _timers;

}

CFRunLoopModeRef代表RunLoop的运行模式

1>、一个RunLoop包含若干个Mode, 每个Mode又包含若干个Souce0/Souce1/Timer/Observer

2>、RunLoop启动时只能选择其中一个Mode, 作为当前模式currentMode

3>、如果需要切换Mode, 只能退出当前Loop, 再重新选择一个Mode进入, 这样不同组的Source0/Source1/Timer/Observer能分隔开来, 互不影响

4>、如果Mode里没有任何Source0/Source1/Timer/Observer, RunLoop会立马退出


mode

在我们的开发中, 有2种Mode最常见:

1.KCFRunLoopDefaultMode(NSDefaultRunLoopMode), 这是App的默认Mode, 通常主线程是在这个Mode下运行

2.UITrackingRunLoopMode, 这是界面跟踪Mode, 用于ScrollView追踪触摸滑动, 保证界面滑动时不受其它Mode影响

五、RunLoop的执行流程

第一步:

通知Observers: RunLoop要开始进入loop了. 紧接着就进入loop.

第二步:

开启一个 do while 来保活线程. 通知Observers: 即将处理Timers

第三步:

通知Observers, 即将处理Sources

第四步:

处理Blocks

第五步:

处理Source0, 可能会再次处理Blocks

第六步:

如果存在Source1, 就跳转到第八步

第七步:

通知Observers, 开始休眠(等待消息唤醒), 进入休眠后, 会等待 mach_port 的消息, 以再次唤醒. 只有在下面四个事件出现时才会被再次唤醒:

基于 port 的 Souce 事件

Timer 时间到

RunLoop超时

被调用者唤醒

第八步:

通知Observer, 结束休眠(被某个消息唤醒), 然后就要开始处理消息了:

如果是 Timer 时间到的话, 就出发 Timer 的回调

如果 dispatch 的话, 就执行 block

如果是 Source1 事件的话, 就处理这个事件

第九步:

处理Blocks

第十步:

根据前面的执行结果, 决定如何操作, 回到第二步, 或者退出 loop

RunLoop的执行流程

相关解释:

· Source0: 触摸事件处理, performSelector:onThread:

· Source1: 基于Port的线程间通信, 系统事件捕捉

· Timers: NSTimers, performSelector:withObject:afterDelay:

· Observers: 用于监听RunLoop的状态, UI刷新(BeforWaiting), Autorelease pool(BeforeWaiting)

六、RunLoop的退出

1.主线程销毁RunLoop退出。

2.Mode中有一些Timer、Source、Observer,这些保证Mode不为空时RunLoop没有空转并且是在运行的,当Mode为空时,RunLoop会立即退出。

3.我们在启动RunLoop的时候可以设置什么时候停止。

你可能感兴趣的:(iOS中的RunLoop)