iOS开发RunLoop学习:一:RunLoop简单介绍

Posted Hello_IOS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发RunLoop学习:一:RunLoop简单介绍相关的知识,希望对你有一定的参考价值。

一:RunLoop的简单介绍

 

 

 

 

 

 

 

 

 

 

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
 *  1:Runloop和线程的关系:1:一一对应,主线程的runloop已经默认创建,但是子线程的需要手动创建:创建子线程的runloop: NSRunLoop *run = [NSRunLoop currentRunLoop];currentRunLoop懒加载的,在同一个子线程中创建多个runloop,则返回的都是同一个对象,因为其是懒加载模式的 2:在runloop中有多个运行模式,但是runloop只能选择一种模式运行,mode里面至少要有一个timer或者是source
    2:1.获得主线程对应的runloop:NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop]; 2:获得当前线程对应的runLoop:NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
    3:CFRunLoop:1:获得主线程对应的runloop:CFRunLoopGetMain() 2:获得当前线程对应的runLoop:CFRunLoopGetCurrent()
 *
 */
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //1.获得主线程对应的runloop
    NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
    
    //2.获得当前线程对应的runLoop
    NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
    
    NSLog(@"%p---%p",mainRunLoop,currentRunLoop);
//    NSLog(@"%@",mainRunLoop);
    
    //Core
    NSLog(@"%p",CFRunLoopGetMain());
    NSLog(@"%p",CFRunLoopGetCurrent());
    
    NSLog(@"%p",mainRunLoop.getCFRunLoop);
    
    //Runloop和线程的关系
    //一一对应,主线程的runloop已经创建,但是子线程的需要手动创建
    [[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil] start];
}

//在runloop中有多个运行模式,但是runloop只能选择一种模式运行
//mode里面至少要有一个timer或者是source
-(void)run
{
    //如何创建子线程对应的runLoop,currentRunLoop懒加载的
    NSLog(@"%@",[NSRunLoop currentRunLoop]);
    NSLog(@"%@",[NSRunLoop currentRunLoop]);
    NSLog(@"run---%@",[NSThread currentThread]);
}

@end

 

 

 

 

 

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
/**
 * 1:NSLog(@"%@",[NSRunLoop currentRunLoop]);打印当前线程的RunLoop,懒加载模式,一条线程对应一个RunLoop对象,有返回,没有创建,主线程的RunLoop默认创建,子线程的RunLoop需要手动创建,[NSRunLoop currentRunLoop],同一个线程中若是创建多个RunLoop,则返回的都是同一个RunLoop对象,一个RunLoop里会有多个mode运行模式(系统提供了5个),但运行时只能指定一个RunLoop,若是切换RunLoop,则需要退出当前的RunLoop
 2:定时器NSTimer问题:1:若是创建定时器用timerWithTimeInterval,则需要手动将定时器添加到NSRunLoop中,指定的运行模式为default,但是如果有滚动事件的时候,定时器就会停止工作。解决办法:更改NSRunLoop的运行模式,UITrackingRunLoopMode界面追踪,此模式是当只有发生滚动事件的时候才会开启定时器。若是任何时候都会开启定时器: NSRunLoopCommonModes, 
   NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode
  占用,标签,凡是添加到NSRunLoopCommonModes中的事件爱你都会被同时添加到打上commmon标签的运行模式上
 
 3:1:scheduledTimerWithTimeInterval此方法创建的定时器默认加到了NSRunLoop中,并且设置运行模式为默认。 2:若是想在子线程开启NSRunLoop:需要手动开启:NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];等到线程销毁的时候currentRunloop对象也随即销毁。2:在子线程的定时器,需要手动加入到runloop:不要忘记调用run方法

 NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
 
 //该方法内部自动添加到runloop中,并且设置运行模式为默认
 [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
 
 //开启runloop
 [currentRunloop run];
 
 
 /*
 *
 */
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//    NSLog(@"%@",[NSRunLoop currentRunLoop]);
    
//    [self timer2];
//    [NSThread detachNewThreadSelector:@selector(timer2) toTarget:self withObject:nil];
    [self timer1];
}

-(void)timer1
{
    //1.创建定时器
   NSTimer *timer =  [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    
    //2.添加定时器到runLoop中,指定runloop的运行模式为NSDefaultRunLoopMode
    /*
     第一个参数:定时器
     第二个参数:runloop的运行模式
     */
//    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    //UITrackingRunLoopMode:界面追踪
   [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

//    NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode
    //占用,标签,凡是添加到NSRunLoopCommonModes中的事件爱你都会被同时添加到打上commmon标签的运行模式上
    /*
     0 : <CFString 0x10af41270 [0x10a0457b0]>{contents = "UITrackingRunLoopMode"}
     2 : <CFString 0x10a065b60 [0x10a0457b0]>{contents = "kCFRunLoopDefaultMode"
     */
//    [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
}

-(void)timer2
{
    NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
    
    //该方法内部自动添加到runloop中,并且设置运行模式为默认
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    
    //开启runloop
    [currentRunloop run];
}

-(void)run
{
    NSLog(@"run-----%@---%@",[NSThread currentThread],[NSRunLoop currentRunLoop].currentMode);
}

@end

####1.Runloop基础知识

- 1.1 字面意思

 

a 运行循环

b 跑圈

 

- 1.2 基本作用(作用重大)

 

a 保持程序的持续运行(ios程序为什么能一直活着不会死)

b 处理app中的各种事件(比如触摸事件、定时器事件【NSTimer】、selector事件【选择器·performSelector···】)

c 节省CPU资源,提高程序性能,有事情就做事情,没事情就休息

 

- 1.3 重要说明

 

        (1)如果没有Runloop,那么程序一启动就会退出,什么事情都做不了。

        (2)如果有了Runloop,那么相当于在内部有一个死循环,能够保证程序的持续运行

        (2)main函数中的Runloop

        a 在UIApplication函数内部就启动了一个Runloop

        该函数返回一个int类型的值

        b 这个默认启动的Runloop是跟主线程相关联的

 

- 1.4 Runloop对象

 

        (1)在iOS开发中有两套api来访问Runloop

            a.foundation框架【NSRunloop】

            b.core foundation框架【CFRunloopRef】

        (2)NSRunLoop和CFRunLoopRef都代表着RunLoop对象,它们是等价的,可以互相转换

        (3)NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要多研究CFRunLoopRef层面的API(Core Foundation层面)

 

 

- 1.5 Runloop参考资料

 

```objc

(1)苹果官方文档

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html

 

(2)CFRunLoopRef开源代码下载地址:

http://opensource.apple.com/source/CF/CF-1151.16/

 

```

 

- 1.6 Runloop与线程

 

1.Runloop和线程的关系:一个Runloop对应着一条唯一的线程

    问题:如何让子线程不死

    回答:给这条子线程开启一个Runloop

2.Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建

3.Runloop的生命周期:在第一次获取时创建,在线程结束时销毁

 

- 1.7 获得Runloop对象

 

```objc

1.获得当前Runloop对象

    //01 NSRunloop

     NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];

    //02 CFRunLoopRef

    CFRunLoopRef runloop2 =   CFRunLoopGetCurrent();

 

2.拿到当前应用程序的主Runloop(主线程对应的Runloop)

    //01 NSRunloop

     NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];

    //02 CFRunLoopRef

     CFRunLoopRef runloop2 =   CFRunLoopGetMain();

 

3.注意点:开一个子线程创建runloop,不是通过alloc init方法创建,而是直接通过调用currentRunLoop方法来创建,它本身是一个懒加载的。

4.在子线程中,如果不主动获取Runloop的话,那么子线程内部是不会创建Runloop的。可以下载CFRunloopRef的源码,搜索_CFRunloopGet0,查看代码。

5.Runloop对象是利用字典来进行存储,而且key是对应的线程Value为该线程对应的Runloop。

 

```

- 1.8 Runloop相关类

 

(1)Runloop运行原理图

 

![PNG](2.png)

 

(2)五个相关的类

 

a.CFRunloopRef

b.CFRunloopModeRef【Runloop的运行模式】

c.CFRunloopSourceRef【Runloop要处理的事件源】

d.CFRunloopTimerRef【Timer事件】

e.CFRunloopObserverRef【Runloop的观察者(监听者)】

 

(3)Runloop和相关类之间的关系图

 

 ![PNG](1.png)

 

(4)Runloop要想跑起来,它的内部必须要有一个mode,这个mode里面必须有source\\observer\\timer,至少要有其中的一个。

 

- CFRunloopModeRef

 

    1.CFRunloopModeRef代表着Runloop的运行模式

    2.一个Runloop中可以有多个mode,一个mode里面又可以有多个source\\observer\\timer等等

    3.每次runloop启动的时候,只能指定一个mode,这个mode被称为该Runloop的当前mode

    4.如果需要切换mode,只能先退出当前Runloop,再重新指定一个mode进入

    5.这样做主要是为了分割不同组的定时器等,让他们相互之间不受影响

    6.系统默认注册了5个mode

        a.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行

            b.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

            c.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

            d.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

            e.kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode

 

以上是关于iOS开发RunLoop学习:一:RunLoop简单介绍的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发RunLoop学习:三:Runloop相关类(source和Observer)

ios开发网络学习六:设置队列请求与RunLoop

iOS开发之RunLoop-赵鹏举

iOS开发之RunLoop-赵鹏举

深入理解RunLoop-转载

iOS开发之RunLoop--转