小小文iOS 开发每天分享优质文章首页投稿(暂停使用,暂停投稿)

RunLoop的基本认识

2016-05-11  本文已影响488人  会跳舞的狮子

RunLoop的介绍

示例.png

RunLoop的基本作用

iOS程序中又两套API来访问和使用RunLoop

1.C语言:Core Foundation -> CFRunLoopRef
2.OC语言的:Foundation -> NSRunLoop
-NSRunLoop 其实是 CFRunLoopRef 的OC包装

#如果想让线程不死,可以手动创建一个RunLoop
void msg(int number)
{
    NSLog(@"runloop被唤醒");
    NSLog(@"执行任务---%d",number);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        do {
            
            NSLog(@"是否有任务需要处理,没有就进入休眠状态");
            NSLog(@"runloop进入到休眠状态");
            int number = 0;
            scanf("%d",&number);
            msg(number);
            
        } while (1);
    }
    return 0;
}

[NSRunLoop currentRunLoop]; //当前的RunLoop
[NSRunLoop mainRunLoop]; //主线程的RunLoop
在子线程需要手动创建RunLoop 创建当前的RunLoop即可
RunLoop是懒加载的
RunLoop是用字典来存储的

RunLoop的相关类

  # 五个相关的类
    a.CFRunloopRef
    b.CFRunloopModeRef【Runloop的运行模式】
    c.CFRunloopSourceRef【Runloop要处理的事件源】
    d.CFRunloopTimerRef【Timer事件】
    e.CFRunloopObserverRef【Runloop的观察者(监听者)】
关系图.png

Mode是运行模式
一个RunLoop包含了若干个模式;
一个运行模型至少要有一个source 或者 Timer
每次RunLoop启动时,只能指定其中一个Mode,这个Mode被称作CurrentMode
如果需要切换Mode ,只能退出线程,重新指定一个Mode进入

1.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
3.UIInitializationRunLoopMode: 在刚启动App时第进入的第一个 Mode,启动完成后就不再使用
4.GSEventReceiveRunLoopMode:接受系统事件的内部Mode,通常用不到
5.kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode

CFRunloopSourceRef (source事件源)

source0 : 非基于端口的
source1: 基于端口的
可以通过打断点的方式查看一个方法的函数调用栈

CFRunloopTimerRef (Timer事件)

/*
    说明:
    (1)runloop一启动就会选中一种模式,当选中了一种模式之后其它的模式就都不鸟。一个mode里面可以添加多个NSTimer,也就是说以后当创建NSTimer的时候,可以指定它是在什么模式下运行的。
    (2)它是基于时间的触发器,说直白点那就是时间到了我就触发一个事件,触发一个操作。基本上说的就是NSTimer
*/
- (void)timer1
{
    //NSTimer 调用了scheduledTimer方法,那么会自动添加到当前的runloop里面去,而且runloop的运行模式kCFRunLoopDefaultMode

    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //更改模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

}

- (void)timer2
{
    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //定时器添加到UITrackingRunLoopMode模式,一旦runloop切换模式,那么定时器就不工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];

    //定时器添加到NSDefaultRunLoopMode模式,一旦runloop切换模式,那么定时器就不工作
    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    //占位模式:common modes标记
    //被标记为common modes的模式 kCFRunLoopDefaultMode  UITrackingRunLoopMode
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

    //    NSLog(@"%@",[NSRunLoop currentRunLoop]);
}

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

- (IBAction)btnClick {

    NSLog(@"---btnClick---");
}

GCD定时器不受RunLoop的Mode的影响

GCD定时器的特点:
1,GCD的定时器不会受到RunLoop运行模式的影响
2,可以控制热舞在主线程还是子线程执行
3,GCD定时器比NSTimer更加准确是因为单位不同:GCD单位是纳秒

//1.创建定时器对象
    /*
     第一个参数:DISPATCH_SOURCE_TYPE_TIMER 创建的是一个定时器
     第四个参数:队列,决定在哪个线程中执行任务
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    
    dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 3.0 *NSEC_PER_SEC);
    
    //2.设置定时器(间隔时间|开始时间)
    /*
     第一个参数:定时器对象
     第二个参数:从什么时候开始计时 DISPATCH_TIME_NOW == 从现在开始
     第三个参数:间隔时间 2.0 以纳秒为单位
     第四个参数:精准度(表示允许的误差)== 0
     */
    dispatch_source_set_timer(timer, t, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    
    //3.定义定时器的工作
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"----GCD----%@",[NSThread currentThread]);
    });
    
    //4.启动执行
    dispatch_resume(timer);

        //注意:dispatch_source_t本质上是OC类,在这里是个局部变量,需要强引用
    self.timer = timer;
.

CFRunloopObserverRef(observer观察者,监听者)

作用: 监听运行循环的状态
如何监听:
1,创建监听对象
2,给RunLoop添加监听者
3,注意对象的释放

 //1.创建监听者
    /*
     第一个参数:分配存储空间
     第二个参数:要监听runloop的什么状态
     第三个参数:持续监听 == YES
     第四个参数:传0
     */
    CFRunLoopObserverRef observer =
CFRunLoopObserverCreateWithHandler
(CFAllocatorGetDefault(),kCFRunLoopAllActivities, YES, 0, 
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        
        /*
         kCFRunLoopEntry = (1UL << 0),         runloop启动
         kCFRunLoopBeforeTimers = (1UL << 1),  runloop即将处理定时器事件
         kCFRunLoopBeforeSources = (1UL << 2), runloop即将处理source事件
         kCFRunLoopBeforeWaiting = (1UL << 5), runloop即将休眠
         kCFRunLoopAfterWaiting = (1UL << 6),  runloop被唤醒
         kCFRunLoopExit = (1UL << 7),          退出
         kCFRunLoopAllActivities = 0x0FFFFFFFU
         */
        
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"runloop启动");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"runloop即将处理定时器事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"runloop即将处理source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"runloop即将休眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"runloop被唤醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"runloop退出");
                break;
                
            default:
                break;
        }
    });
    
    
    //2.设置监听的runloop和运行模式
    /*
     第一个参数:runloop对象
     第二个参数:监听者
     第三个参数:runloop的运行模式
     第四个参数:
     */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    
    //3.需要释放
    CFRelease(observer);

RunLoop处理逻辑-官方.png

上图显示了线程的输入源

1.基于端口的输入源(Port Sources)
2.自定义输入源(Custom Sources)
3.Cocoa执行Selector的源(performSelectorxxxx
方法)
4.定时源(Timer Sources )

线程针对上面不同的输入源,有不同的处理机制

1.handlePort——处理基于端口的输入源
2.customSrc——处理用户自定义输入源
3.mySelector——处理Selector的源
4.timerFired——处理定时源

RunLoop的应用场

应用场景.png
#Author: 会跳舞的狮子
上一篇下一篇

猜你喜欢

热点阅读