浅谈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的功能 

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

功能二:解决CPU空转

刚才说到了岛国动作片,下载是个异步的过程,我们来看下我们常用的框架AFNetworking里面用到的RunLoop,



打开AFNetworking发现RunLoop跟NSTimer 一起使用, 我们还发现了 一个现象就是 RunLoop后一个模式;NSTimer 的用法先不扯,我们先看下这个RunLoop的模式。。。我现在分别在main.m 和Appdelegate.m 还有ViewController.m 的viewDidLoad和viewDidAppear方法中 中加入以下代码

 NSRunLoop *main = [NSRunLoop 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种模式输出出来

UITrackingRunLoopMode,
GSEventReceiveRunLoopMode,
kCFRunLoopDefaultMode,///默认模式
UIInitializationRunLoopMode,
kCFRunLoopCommonModes


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


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

我的源码如下

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    startcount = 0;
    UIButton *button1 = [[UIButton alloc ] init];
    button1.frame = CGRectMake(10, 140, 100, 50);
    [button1 setTitle:@"启动定时器" forState:UIControlStateNormal];
    [button1 addTarget:self action:@selector(button1) forControlEvents:UIControlEventTouchUpInside];
    button1.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:button1];
    
    
    button1 = [[UIButton alloc ] init];
    button1.frame = CGRectMake(150, 140, 100, 50);
    [button1 setTitle:@"暂停定时器" forState:UIControlStateNormal];
    [button1 addTarget:self action:@selector(button2) forControlEvents:UIControlEventTouchUpInside];
    button1.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:button1];
    
    UIScrollView *scroll = [[UIScrollView alloc] init];
    scroll.frame = CGRectMake(0, 200, 320, 300);
    scroll.backgroundColor = [UIColor redColor];
    scroll.contentSize = CGSizeMake(320, 900);
    scroll.delegate = self;
     [self.view addSubview:scroll];
    
    timer =  [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(function:) userInfo:nil repeats:YES];
    [timer setFireDate:[NSDate distantFuture]];
}

-(void)function:(id)sender{
    
    self.title = [NSString stringWithFormat:@"%d",startcount++];
    [self prinitMode:@"Timer"];
   
}

-(void)button1{
   [timer setFireDate:[NSDate distantPast]];
    
}
-(void)button2{
    [timer setFireDate:[NSDate distantFuture]];
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    
    [self prinitMode:@"scrollViewDidScroll"];
}

-(void)prinitMode:(NSString *)modeName{
    
    NSRunLoop *main = [NSRunLoop mainRunLoop];
    
    NSLog(@"mainRunLoop %@  %@",modeName,main.currentMode);
    
    CFRunLoopRef runloopm = CFRunLoopGetMain();
    NSArray *allModesm = CFBridgingRelease(CFRunLoopCopyAllModes(runloopm));
    NSLog(@"mainRunLoop %@ ALL  %@  ",modeName,allModesm);

    NSRunLoop *current = [NSRunLoop currentRunLoop];
    
    NSLog(@"currentMode %@ %@",modeName,current.currentMode);
    
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runloop));
    
    NSLog(@"current %@ ALL %@  ",modeName,allModes);

}

然后看下我的日志文件 


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

timer =  [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(function:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[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负责处理消息事件,即输入源事件和计时器事件





















  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值