使用范例
-(NSThread *)networkRequestThread{
//单例创建线程对象
static NSThread *networkRequestThread;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//创建的同时启动该线程runloop
networkRequestThread = [[NSThread alloc]initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[networkRequestThread start];
});
return networkRequestThread;
}
//线程入口
-(void) networkRequestThreadEntryPoint:(id)__unused object{
//自动释放池,保证里面的autorelease对象能够在出了作用范围外就可以自动释放,而不是一直等到runloop结束
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
//获取当前线程runloop
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
//给runloop添加一个端口作为输入源,用于线程间通信
[currentRunloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[currentRunloop run];
}
}
使用runloop情景:
开启一个常驻线程,能够保持长时间去处理各种事件。
NSTimer CADisplayLink 通过向Runloop注册来开启定时任务
GCD的延时操作,UI界面刷新,响应用户的触摸事件
addPort:{NSMMachPort port】这里将输入源端口添加到runloop里,通过输入源端口相当于一个载体,用于进行线程间消息的传递。
线程间通信方式:通过NSMachPort端口
将端口添加到当前线程的runloop里,在runloop的消息循环机制中去接受通过端口传递过来的消息。也可以通过端口去给其他线程发送消息。
RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下重启成新的。利用这个机制,ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动:只能在NSDefaultRunLoopMode模式下处理的事件会影响scrllView的滑动。
如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。
同时因为mode还是可定制的,所以:
Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。
1、如果是在主线程中运行timer,想要timer在某界面有视图滚动时,依然能正常运转,那么将timer添加到RunLoop中时,就需要设置mode 为NSRunLoopCommonModes。
2、如果是在子线程中运行timer,那么将timer添加到RunLoop中后,Mode设置为NSDefaultRunLoopMode或NSRunLoopCommonModes均可,但是需要保证RunLoop在运行,且其中有任务。
伪代码
function loop() {
initialize();
do {
var message = get_next_message();
process_message(message);
} while (message != quit);
}
CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类
CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息,CADisplayLink类对应的selector就会被调用一次。
22.runloop和线程有什么关系
runloop与线程是一一对应的
对于主线程来说,runloop在默认启动的。
对于子线程来说,runloop是懒加载的,只有当我们手动获取的时候才会创建。
当线程的 runloop 被打开后就能保持着接受并处理各种消息,而不会中途退出(有任务就去接受处理,没有就处于休眠状态)。
23.runloop的mode作用是什么
model 按照优先级可分为:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,通常主线程在这个 Mode 下运行
UITrackingRunLoopMode:保证Scrollview滑动顺畅
UIInitializationRunLoopMode:启动程序后的过渡mode,启动完成后就不再使用
NSRunLoopCommonModes :占位mode,可以向其中添加其他mode
24.以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决
(RunLoop只能运行在一种mode下,切换mode的时候runloop会有一个重启的动作。
ScrollView滚动过程中NSDefaultRunLoopMode的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动。NSTimer 对象默认是以NSDefaultRunLoopMode注册到runloop里的,所以切换的时候导致NSTimer将不再被调度。所以需要将NSTimer以 NSRunLoopCommonModes 的方式注册到Runloop中才能保持一致调用。)
RunLoop 理解成下面循环
1.休眠状态
2.接收到要处理的消息,被唤醒
3.对消息进行处理
4.消息处理完毕之后,回到休眠状态
1(等待消息) -> 2(将要处理消息) -> 3(处理消息) -> 4(消息处理完成) -> 1(等待消息)
对于每一个 runloop 系统会默认为期创建一个自动释放池。
AutoreleasePool 在 4 (消息处理完成) 这个阶段进行释放。iOS 开发者写的程序,无论包含多少层函数调用,也一直处于 3 (处理消息) 这个阶段当中,其余阶段属于 UI 框架的一部分。因此在当前消息循环内,放到 AutoreleasePool 的对象会一直生效,并不会被释放
入口函数作用
- 初始化UIApplication单例对象
- 初始化AppDelegate对象,并设为UIApplication对象的代理
- 建立一个主事件循环,其中包含UIApplication的Runloop来开始处理事件。
runloop退出的条件:app退出;线程关闭;
当我们去启动 RunLoop,系统会自动在内部创建自动释放池。