首页 > 代码库 > 浅谈iOS中的RunLoop

浅谈iOS中的RunLoop

首先解释下为什么是浅谈,主要是RunLoop这个东西不单单是iOS的范畴,还涉及到操作系统,我指的浅谈仅仅针对ios上层应用,底层的东西概不涉及 ,所以只能浅谈浅谈了。

在浅谈RunLoop之前我们来写个小demo,超级简单,一个按钮,然后给按钮一个断点

技术分享

这块标记了1,2,3,4 红色的字;其实这是这个APP启动的一个过程


但是说好了浅谈RunLoop为啥又扯到APP的启动了 ? 先不要在意这些细节。。。 我先来解释下我标出的1,2,3,4分别是啥东西

1,dyld :这是啥子鬼了? the dynamic link editor (动态链接编辑器)

    我们惊奇的发现一个APP启动的时候是先干这个事,我们把xcode稍作配置

   技术分享  第一步

 然后

技术分享


我把这个鬼复制下来 dyld_PRINT_STATISTICS 打上对勾,后面的参数设为1

然后打开另外一个开关

技术分享

一切准备好之后,我们启动我们的APP,注意在真机上启动。。。。  然后我们看下控制台打印的东西


技术分享


技术分享

技术分享

技术分享

看了这些日志输出我们对dyld有那么一点初级的概念了,原来这货是加载项目所需要的开发包,第一步算介绍到这里,然后我们看第二步


2,main() :整个APP的入口,将AppDelegate扯进来

技术分享


3,RunLoop 

我们不难发现main函数执行之后并不是直接执行我们的以UI开头的事件或方法,而是先搞一个RunLoop ,也就是RunLoop在用户界面呈现之前就启动了。。。。。 ,我们先继续,我们说下第4条


4,用户层操作 

我们可以看到 UIApplication UIWindow UIControl 等我们熟悉的代码,但是不幸的是他们却在RunLoop之后运行。。。。 那么RunLoop到底是个啥子。。。。





RunLoop: 从字面的意思上来看 运行循环,在我大iOS的范畴中又是个啥意思了,意如其字 ; 如果你还看不懂,那我只能点拨下了一师是个”女子“学校  ,换句话说就是 一个APP启动的时候 相当于启动了一个死循环,而这个死循环就是RunLoop,我们在应用里面写的所有代码都在RunLoop里面运行; 如果你搞过单片机就很好懂啦,代码直接来个while(1){  } ,RunLoop其实就是这么回事,解释一下为什么我们的代码是在一个死循环里面运行

1.我手机启动一个APP,不让手机黑屏,过一段时间(几小时或几天)我再点击了,这个APP肯定响应我的动作

2.我再APP里面写一个图片轮播,不管过多久他一直在轮播,谁在支撑他?

3.给个岛国动作片的地址,我们手机干别的事情了,却能自动的下载下来。。。

我们的APP时刻保持用户响应其实就是开启了一个主RunLoop , 这个主RunLoop 是负责这个APP活动的心脏

技术分享

这就是RunLoop   ,我们来总结下RunLoop的功能 

功能一:用来处理耗时长的异步事件

 mainRunLoop];  

  • NSLog(@"int main %@",main.currentMode);  
  •          
  • NSRunLoop *current = [NSRunLoop currentRunLoop];  
  • NSLog(@"int main %@",current.currentMode);  
  •          
  • CFRunLoopRef runloop = CFRunLoopGetCurrent();  
  • NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runloop));  
  • NSLog(@"int main %@  ",allModes);  
  •          
  •          
  • CFRunLoopRef runloopm = CFRunLoopGetMain();  
  • NSArray *allModesm = CFBridgingRelease(CFRunLoopCopyAllModes(runloopm));  
  • NSLog(@"int main %@  ",allModesm);  


技术分享

我们来看下输出的结果。。 

技术分享


我们来总结下,总结之前我们把5种模式输出出来

 

  1. UITrackingRunLoopMode,  
  2. GSEventReceiveRunLoopMode,  
  3. kCFRunLoopDefaultMode,///默认模式  
  4. UIInitializationRunLoopMode,  
  5. kCFRunLoopCommonModes  

 

技术分享

我们惊奇的发现,我们一般页面用到两种默认,Init模式和Default模式;也就是页面初始化的是时候是init模式执行完ViewDidLoad方法之后RunLoop变为了Default模式了 ,注意这里有两个RunLoop, 一个是MainRunLoop 和CurrentRunLoop,MainRunLoop无可厚非就是整个项目的主RunLoop,Current就是当前的,因为当前只有一个线程,所以MainRunLoop 和CurrentRunLoop是一个RunLoop;为啥要说Mode了 ,我们来看一个例子。

技术分享

我启动一个定时器,每秒中自增1, 看标题;每当我手放在ScrollView上了,定时器就不工作了。。

我的源码如下

 

  1. - (void)viewDidLoad {  
  2.     [super viewDidLoad];  
  3.     // Do any additional setup after loading the view.  
  4.     self.view.backgroundColor = [UIColor whiteColor];  
  5.     startcount = 0;  
  6.     UIButton *button1 = [[UIButton alloc ] init];  
  7.     button1.frame = CGRectMake(10, 140, 100, 50);  
  8.     [button1 setTitle:@"启动定时器" forState:UIControlStateNormal];  
  9.     [button1 addTarget:self action:@selector(button1) forControlEvents:UIControlEventTouchUpInside];  
  10.     button1.backgroundColor = [UIColor lightGrayColor];  
  11.     [self.view addSubview:button1];  
  12.       
  13.       
  14.     button1 = [[UIButton alloc ] init];  
  15.     button1.frame = CGRectMake(150, 140, 100, 50);  
  16.     [button1 setTitle:@"暂停定时器" forState:UIControlStateNormal];  
  17.     [button1 addTarget:self action:@selector(button2) forControlEvents:UIControlEventTouchUpInside];  
  18.     button1.backgroundColor = [UIColor lightGrayColor];  
  19.     [self.view addSubview:button1];  
  20.       
  21.     UIScrollView *scroll = [[UIScrollView alloc] init];  
  22.     scroll.frame = CGRectMake(0, 200, 320, 300);  
  23.     scroll.backgroundColor = [UIColor redColor];  
  24.     scroll.contentSize = CGSizeMake(320, 900);  
  25.     scroll.delegate = self;  
  26.      [self.view addSubview:scroll];  
  27.       
  28.     timer =  [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(function:) userInfo:nil repeats:YES];  
  29.     [timer setFireDate:[NSDate distantFuture]];  
  30. }  
  31.   
  32. -(void)function:(id)sender{  
  33.       
  34.     self.title = [NSString stringWithFormat:@"%d",startcount++];  
  35.     [self prinitMode:@"Timer"];  
  36.      
  37. }  
  38.   
  39. -(void)button1{  
  40.    [timer setFireDate:[NSDate distantPast]];  
  41.       
  42. }  
  43. -(void)button2{  
  44.     [timer setFireDate:[NSDate distantFuture]];  
  45. }  
  46.   
  47. -(void)scrollViewDidScroll:(UIScrollView *)scrollView{  
  48.       
  49.     [self prinitMode:@"scrollViewDidScroll"];  
  50. }  
  51.   
  52. -(void)prinitMode:(NSString *)modeName{  
  53.       
  54.     NSRunLoop *main = [NSRunLoop mainRunLoop];  
  55.       
  56.     NSLog(@"mainRunLoop %@  %@",modeName,main.currentMode);  
  57.       
  58.     CFRunLoopRef runloopm = CFRunLoopGetMain();  
  59.     NSArray *allModesm = CFBridgingRelease(CFRunLoopCopyAllModes(runloopm));  
  60.     NSLog(@"mainRunLoop %@ ALL  %@  ",modeName,allModesm);  
  61.   
  62.     NSRunLoop *current = [NSRunLoop currentRunLoop];  
  63.       
  64.     NSLog(@"currentMode %@ %@",modeName,current.currentMode);  
  65.       
  66.     CFRunLoopRef runloop = CFRunLoopGetCurrent();  
  67.     NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runloop));  
  68.       
  69.     NSLog(@"current %@ ALL %@  ",modeName,allModes);  
  70.   
  71. }  


然后看下我的日志文件 

技术分享

我们发现手放下UIScrollView的时候RunLoop的模式变成了 UITrackingRunLoopMode ,定时器不起作用; 手离开时,模式变成KCFRunLoopDefaultMode ,定时器继续工作。那么这个就比较麻烦了,滑动的时候定时器不起作用,现实项目中这个肯定不行的。。 如何解决了 ?  刚刚我们说到了AFNetworking的源码,我们可以模仿下源码

 

  1. timer =  [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(function:) userInfo:nil repeats:YES];  
  2. [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];  
  3. [timer setFireDate:[NSDate distantFuture]];  

我在timer上面加了这段代码,惊奇的发现,现在我滑动UIScrollView,Timer并没有收到影响;我看来下日志,

开启定时的时候: mainRunLoop Timer  kCFRunLoopDefaultMode

滑动UIScrollView的时候: mainRunLoop scrollViewDidScroll  UITrackingRunLoopMode

再次松开UIScrollView的时候:mainMode Timer UITrackingRunLoopMode
 

 

UITrackingRunLoopMode:用户滑动Mode

GSEventReceiveRunLoopMode:用于系统接收事件Mode

kCFRunLoopDefaultMode:RunLoop默认Mode

UIInitializationRunLoopMode:初始化Mode

kCFRunLoopCommonModes:通用Mode


这就解释了为什么AFNetworking中使用RunLoop来开启一个新的timer,上面提到一个项目中的主RunLoop只有一个(有点类似于主线程和子线程,主线程只有一个,子线程有很多个),每次新建一个Timer我们需要开启一个子的RunLoop 然后加入到主RunLoop中

总结下RunLoop的一些特点

1.主线程的RunLoop在应用启动的时候就会自动创建

2.其他线程则需要在该线程下自己启动

3.不能自己创建RunLoop

4.RunLoop并不是线程安全的,所以需要避免在其他线程上调用当前线程的RunLoop

5.RunLoop负责管理autorelease pools

6.RunLoop负责处理消息事件,即输入源事件和计时器事件

浅谈iOS中的RunLoop