面试题
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的时候可以设置什么时候停止。
网友评论