RunLoop个人理解

目前,在对iOS的面试中总是离不开这2个问题。

1、Runloop
2、Runtime

下面是我对与RunLoop的一些理解,

1.什么是RunLoop?

RunLoop,翻译过来就是运行循环,可以简单的理解为一个while循环,当然这么说是不对的,但对于RunLoop来说,while循环算是对其工作模式的简单展示。在一个线程中执行一定的操作后,如果没有RunLoop程序执行完毕就会立即退出,如果有RunLoop程序会一直运行,并且时时刻刻在等待用户的输入操作。RunLoop可以在需要的时候自己跑起来运行,在没有操作的时候就停下来休息。充分节省CPU资源,提高程序性能。

但是! 即便RunLoop就普通开发而言,很难会用到很深的东西。不出意外的话,大多数的开发者接触到RunLoop的契机都是NSTimer(O-C)、Timer(Swift)的计时在滑动页面的时候没有正常工作。

2.RunLoop具体用来干什么?

1.保证RunLoop所在线程不关闭
多线程工作中,在每条线程创建的时候都会有一个默认的没有开启的RunLoop,当RunLoop没有run时,任务完成则关闭线程,当run时进入运行循环,线程不会关闭。额外需要说到的一点是GCD本身对RunLoop有封装 相对于NSThread来说不需要[runloop run]这一步。

2.负责监听事件(触摸,计时器,网络等)
RunLoop自然不是只用来保证线程不关闭,当一个APP在运行时,我们对APP做的所有交互都需要使用RunLoop来捕捉。它负责了监听触摸(点击,拖动等)、计时器(Timer)、网络(发送的请求收到了回复)等事件。

3.RunLoop在开发工作中的使用
前边说到了计时器在滑动页面的时候没有正常工作的问题,那么为什么会出现这种问题呢?

首先看代码,在没有接触RunLoop前,可能都会这样写

        //OC

        NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(oneMethod) userInfo:nil repeats:true];

        [[NSRunLoop currentRunLoop]addTimer:timer forMode:(NSDefaultRunLoopMode)];

        //Swift

        let timer = Timer.init(timeInterval:1, target:self, selector: #selector(oneMethod), userInfo:nil, repeats:true)

        RunLoop.current.add(timer, forMode: .default)

于是出现了页面滑动,计时器没有触发的问题。对于这个问题,这就要说到RunLoopMode的问题了,在iOS的RunLoop中共有五种RunLoopMode,其中我们能够接触到的就只有3种,即NSDefaultRunLoopMode(默认)、UIRrackingRunLoopMode(UI模式)、NSRunLoopCommonModes(混合模式)。在上述代码中,使用的都是NSDefaultRunLoopMode,作为一个default的mode,自然是优先级比较低。事实上,每当UI发生改变时,便会触发UIRrackingRunLoopMode,此时便将不会触发NSDefaultRunLoopMode下的oberservce,source,timer。为了解决这一问题,便有了NSRunLoopCommonModes,该模式下触发UIRrackingRunLoopMode时,同时可以触发NSRunLoopCommonModes下的oberservce,source,timer。

所以上述代码改一下RunLoopMode即可

    //OC
    NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(oneMethod) userInfo:nil repeats:true];
    [[NSRunLoop currentRunLoop]addTimer:timer forMode:(NSRunLoopCommonModes)];
    //Swift
    let timer = Timer.init(timeInterval: 1, target: self, selector: #selector(oneMethod), userInfo: nil, repeats: true)
    RunLoop.current.add(timer, forMode: .common)

现在对于单纯的RunLoop的简单介绍就结束了。
4.RunLoop的更深层用法
想更深层的使用RunLoop就要使用到CFRunLoop了,以下代码实现对当前的RunLoop的监听,kCFRunLoopBeforeWaiting这一类的CFRunLoopActivity可以按需填写,每次循环都会在NSDefaultRunLoopMode触发Callback回调。

 - (void)addRunloopObserver{//添加观察者
    CFRunLoopRef runloop = CFRunLoopGetCurrent();
    static CFRunLoopObserverRef defaultModeObserver;
    CFRunLoopObserverContext context = {
        0,
        (__bridge void *)(self),
        &CFRetain,
        &CFRelease,
        NULL
    };
    defaultModeObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting, YES, 0, &Callback, nil);
    
    CFRunLoopAddObserver(runloop, defaultModeObserver, kCFRunLoopDefaultMode);
    
 }

 static void Callback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){//触发CFRunLoopActivity时的回调
    
 }

那我们能做什么呢?简单的说一个例子就是在tableview滑动的时候加载图片的操作放入一个task数组中每次回调时进行耗时操作。即在callback中实现[cell.contentView addSubview:]的操作
但是这样优化虽然在拖拽的时候不再卡顿,但会出现拖拽的时候不加载图片的问题,那么结合上边的RunLoopMode的知识该怎么改相比应该就很清楚了吧。

你可能感兴趣的:(RunLoop个人理解)