zoukankan      html  css  js  c++  java
  • Runloop运行循环的理解

     runloop运行流程图

    系统默认注册了5个Mode:
    kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
    
    UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    
    UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
    
    GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
    
    kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
    
    CFRunLoopModeRef代表RunLoop的运行模式
    一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer
    
    每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode
    
    如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入
    
    这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响
    

      

    定时器

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //         定时器可以运行
            NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:YES];
            
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        });
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //         定时器无法运行
            NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:YES];
            
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:YES];
    //         定时器无法运行
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        });
        
        dispatch_async(dispatch_get_main_queue(), ^{
            NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:YES];
    //         定时器可以运行
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
            
        });
        
    

     结论:如果定时器在主线程中开启,可以正常运行;定时器在子线程中开启,无法正常运行; 如果对应线程没有 RunLoop 该方法也会失效,也就是说currentRunloop中 没有timer,没有source,也没有OBServer,添加 [NSRunLoop currentRunLoop] run]试试;  主线程中能够运行是因为timer添加到runloop中后,主线程runloop默认是启动的,子线程中的runloop添加的timer,runloop需要手动启动.

    Runloop要启动要素:1.runloop中要有timer | source | observer其中一个条件 2.runloop得自己启动

    常驻线程

    实例:开启一个线程,不让线程退出,这个线程一直在接受任务的处理,当一有任务,线程就接受处理,没有任务就休眠

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.thread = [[HJThread alloc] initWithTarget:self selector:@selector(invoke) object:nil];
        [self.thread start];
    }
    
    - (void)invoke
    {
      @autoreleasepool{ NSLog(@"******invoke*****%@", [NSThread currentThread]);    // 添加一个port让runloop可以运行循环 [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run];
      } NSLog(@"************"); } - (void)touchInvoke { NSLog(@"*********touchInvoke*********%@", [NSThread currentThread]); NSLog(@"%@", [NSRunLoop currentRunLoop]); }
    // 屏幕点击 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self performSelector:@selector(touchInvoke) onThread:self.thread withObject:nil waitUntilDone:NO]; }

     注意:经测试子线程虽然在defaultMode,但是拖动UIScrollView时并不会阻塞当前子线程的runloop defalutMode,因为拖动的view是在主线程的模式UITrackingMode,2个线程的模式互不干扰

    停止runloop

    1.需要保存当前runloop

    2.使用CF函数开启运行runloop

    3.使用CF函数停止runloop

    #import "ViewController.h"
    
    @interface ViewController () <NSURLConnectionDataDelegate>
    /** runLoop */
    @property (nonatomic, assign) CFRunLoopRef runLoop;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 如果在子线程中使用NSURLConnection发送请求是不会有效果,因为子线程的runloop没有启动,子线程runloop默认是不启动的
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSURLConnection *conn = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com/images/234234324limgAB/2342lkjasdf3kkkkk.jpg"]] delegate:self];
            // 决定代理方法在哪个队列中执行
            [conn setDelegateQueue:[[NSOperationQueue alloc] init]];
            
            // 启动子线程的runLoop
    //        [[NSRunLoop currentRunLoop] run];
            
            // 保存当前runloop
            self.runLoop = CFRunLoopGetCurrent();
            
            // 启动runLoop
            CFRunLoopRun();
        });
    }
    
    #pragma mark - <NSURLConnectionDataDelegate>
    - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
        NSLog(@"didReceiveResponse******%@", [NSThread currentThread]);
    }
    
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        
        NSLog(@"didReceiveData******%@", [NSThread currentThread]);
    }
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        NSLog(@"connectionDidFinishLoading******%@", [NSThread currentThread]);
        
        // 停止RunLoop
        CFRunLoopStop(self.runLoop);
    }
    
    @end
    

      

    定时器自动调度

    //     任务自动调度,无需手动fire
        [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:YES];
        
        ------------------2种方式等价------------------
        
    //    任务自动调度,无需手动fire
        NSTimer *timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(run) userInfo:nil repeats:YES];
        //         定时器可以运行
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    

    监听runloop运行循环的事件状态变化,可以用以拦截一些事件的处理

    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),        // : 即将到 runloop
        kCFRunLoopBeforeTimers = (1UL << 1), // : 即将处理 timer 之前
        kCFRunLoopBeforeSources = (1UL << 2),// : 即将处理 source 之前
        kCFRunLoopBeforeWaiting = (1UL << 5),// : 即将休眠
        kCFRunLoopAfterWaiting = (1UL << 6), // : 休眠之后
        kCFRunLoopExit = (1UL << 7),         // : 退出
        kCFRunLoopAllActivities = 0x0FFFFFFFU// : 所有的活动
    };
    
    - (IBAction)btnClick:(id)sender {
        NSLog(@"btnDidClick*****");
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
      // 监听runloop状态变化 [self observer]; } - (void)observer { // 创建observer CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { NSLog(@"****监听到RunLoop状态发生改变**%zd", activity); }); // 添加观察者:监听RunLoop的状态 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); // 释放Observer CFRelease(observer); }

     

    Autorelease Pool

    为啥OC程序的Main函数使用autorelease pool包括了,从运行循环中也可以解释,运行循环中不断的在接受和处理事件,中间的会产生很多的变量和资源,产生的这些变量和资源会放到自动释放池中,因runloop一直没有退出,那么变量就可能没有被释放,但是加上了autorelease pool后,在runloop进入休眠前时,autorelease pool就会释放临时变量和资源,这样内存就可以得到管理; runloop重新运行时就又会创建一个自动释放池

    自动释放池会再在Runloop休眠前(beforeWait)释放,又会紧接着创建一个新的自动释放池,用以下次唤醒时使用

    Runloop应用:

    .NSTimer

    .ImageView显示

    .PerformSelecor 可以给线程发送消息

    .常驻线程,开启一个常驻线程,让线程不销毁,等待其他线程发送消息,然后处理任务和事件

    >在子线程中开启一个定时器

    >在子线程中进行一些长期监控,语音通话,或是传输数据等业务场景

    .自动释放池

    .可以添加Observer监听Runloop的状态,这样可以拦截一些事件处理,比如过滤器功能等.

    .可以让某些事件(行为,任务)在特定的模式下执行

    总结:

    >运行循环,跑圈. 可以查看源码里面内部是一个do while循环,循环内部不断处理任务和事件(source,timer,observer)

    >创建开启运行要素最少得要有source(消息源,source0,source1),timer中的一个条件

    >一个线程对应一个Runloop(底层是通过字典保存Runloop,线程作为key,Runloop作为value),主线程的Runloop默认已经开启,子线程的Runloop的手动启动,通过调用[runloop run]方法启动

    >Runloop只能选择一个Mode模式启动,如果当前模式Mode没有任何Source(消息源),timer,Runloop就会直接退出

    >当kCFRunLoopEntry会创建新的释放池用以Runloop被唤醒时使用,自动释放池在runloop即将进入休眠时(kCFRunLoopBeforeWaiting)释放或kCFRunLoopExit退出时,自动释放池释放 

    >子线程runloop默认是不启动的,如果子线程runloop需要手动启动

     可以参考文献:

    http://www.cocoachina.com/ios/20150601/11970.html

  • 相关阅读:
    使用Mint-UI的Loadmore实现上拉加载更多和下拉刷新
    JavaScript的日常所得
    web网站性能优化整理
    ArrayBuffer
    Blob
    FormData
    FileReader
    websocket的实践
    Vue CLI 3的Vue.config.js
    css行高line-height的一些深入理解及应用
  • 原文地址:https://www.cnblogs.com/HJiang/p/7476339.html
Copyright © 2011-2022 走看看