RunLoop

2019-12-11  本文已影响0人  纳兰沫

讲讲 RunLoop,项目中有用到吗?

1.控制线程生命周期(线程保活)
2.解决NSTimer在滑动时停止工作的问题
3.监控应用卡顿
4.性能优化

RunLoop内部实现逻辑?

01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop

RunLoop和线程的关系?

1.每条线程都有一个与之相对应的RunLoop对象
2. RunLoop保存在一个全局的Dictionary里面 线程作为key,RunLoop作为value
3.线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
4. RunLoop会在线程结束的时候销毁
5.主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

timer 与 RunLoop 的关系?

- RunLoop里面有一个变量_modes 里面存放着_timers
- 如果timer设置为commonModes 会把timer放在RunLoop的变量_commonModeItems里面
- 在RunLoop的运行逻辑里面进行工作的
RunLoop的结构.png
CFRunLoopMode的结构.png

程序中添加每3秒响应一次的NSTimer,当拖动tableview时timer可能无法响应要怎么解决?

创建 timer 把他添加到RunLoop的commonModes中

RunLoop 是怎么响应用户操作的, 具体流程是什么样的?

- 由Source1把系统事件进行捕捉  把事件包装成事件队列EvenetQueue
- EvenetQueue是由Source0处理的

说说RunLoop的几种状态

 kCFRunLoopEntry
 kCFRunLoopBeforeWaiting
 kCFRunLoopBeforeTimers
 kCFRunLoopBeforeSources
 kCFRunLoopAfterWaiting
 kCFRunLoopExit

RunLoop的mode作用是什么?

1.kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

RunLoop

在程序运行过程中循环做一些事情

应用范畴

1.定时器(Timer)、PerformSelector
2.GCD Async Main Queue
3.事件响应、手势识别、界面刷新
4.网络请求
5.AutoreleasePool

RunLoop的基本作用

- 保持程序的持续运行
- 处理App中的各种事件(比如触摸事件、定时器事件等)
- 节省CPU资源,提高程序性能:该做事时做事,该休息时休息

RunLoop和线程的关系

1.每条线程都有一个与之相对应的RunLoop对象
2. RunLoop保存在一个全局的Dictionary里面 线程作为key,RunLoop作为value
3.线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
4.RunLoop会在线程结束的时候销毁
5.主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
//Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

//Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
image.png image.png

CFRunLoopModeRef

CFRunLoopModeRef 代表RunLoop的运行模式
- 一个RunLoop包含若干个Mode  每个Mode又包含若干个Source0/Source1/Timer/Observer
- RunLoop启动时只能选择其中一个Mode 作为currentMode
- 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
  不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
- 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
常见的Mode
1.kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

NSRunLoopCommonModes并不是一个真的模式 他只是一个标记
timer能在commonModes数组中存放的模式下工作

RunLoop的Mode包含的内容

- Source0
  a.触摸事件处理
  b.performSelector:onThread:
- Source1
  a.基于Port的线程间通信
  b.系统事件捕捉
- Timers
  a.NSTimer
  b.performSelector:withObject:afterDelay:
- Observers
  a.用于监听RunLoop的状态
  b.UI刷新(BeforeWaiting)
  c.Autorelease pool(BeforeWaiting)

CFRunLoopObserverRef

监听observer
void observerRunLoopActivicity(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"kCFRunLoopEntry");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"kCFRunLoopBeforeWaiting");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"kCFRunLoopBeforeTimers");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"kCFRunLoopBeforeSources");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"kCFRunLoopAfterWaiting");
            break;
        case kCFRunLoopExit:
            NSLog(@"kCFRunLoopExit");
            break;
        default:
            break;
    }
}
//创建observer
//CFOptionFlags activities 监听什么状态
//Boolean repeats 是否重复
//CFIndex order  顺序
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities , YES, 0, observerRunLoopActivicity, NULL);
//kCFRunLoopCommonModes 包括 kCFRunLoopDefaultMode  和 UITrackingRunLoopMode
//添加observer到RunLoop中
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
//释放observer
CFRelease(observer);
第二种添加observer
void observerRunLoopActivicity(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"kCFRunLoopEntry");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"kCFRunLoopBeforeWaiting");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"kCFRunLoopBeforeTimers");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"kCFRunLoopBeforeSources");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"kCFRunLoopAfterWaiting");
            break;
        case kCFRunLoopExit:
            NSLog(@"kCFRunLoopExit");
            break;
        default:
            break;
    }
}

//创建Observer
CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        
});
//添加observer到RunLoop中
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
//释放observer
CFRelease(observer);
运行逻辑.png

RunLoop休眠的实现原理

实现原理.png
while死循环还是要不断的执行代码 这个时候程序没有处于休眠状态

实际用途

1.控制线程生命周期(线程保活)
2.解决NSTimer在滑动时停止工作的问题
3.监控应用卡顿
4.性能优化
线程保活
class ViewController: UIViewController {

    var thread: MJThread?
    override func viewDidLoad() {
        super.viewDidLoad()
        
        thread = MJThread(target: self, selector: #selector(run), object: nil)
        thread?.start()
    }

    //为了保证线程保活
    @objc func run() {
        
        print(#function + "------------", Thread.current)
        //往RunLoop里面添加Source/Timer/Observer 这样RunLoop才不会退出
        RunLoop.current.add(Port(), forMode: .default)
        RunLoop.current.run()
    }
    
    //线程里面真正要做的事情
    @objc func test() {
        print(#function + "------------", Thread.current)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        perform(#selector(test), on: thread!, with: nil, waitUntilDone: false)
    }

}

RunLoop.current.add(Port(), forMode: .default)
RunLoop.current.run()
会开启一个无限的循环且无法停止 专门用于开启一个永不销毁的线程(RunLoop)

会开启一个无限的循环 无法停止
thread = MJThread(block: {
            RunLoop.current.add(Port(), forMode: .default)
            RunLoop.current.run()
            print("end-------" + Thread.current.description)
        })
 thread?.start()

 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        perform(#selector(test), on: thread!, with: nil, waitUntilDone: false)
    }
    
   //线程里面真正要做的事情
    @objc func test() {
        print(#function + "------------", Thread.current)
    }
    deinit {
        print(#function)
        perform(#selector(stop), on: thread!, with: nil, waitUntilDone: false)
        thread = nil
    }
    
    //用于停止子线程的RunLoop
    @objc func stop() {
        CFRunLoopStop(CFRunLoopGetCurrent())
    }
设置一个BOOl类型变量停止RunLoop
thread = MJThread(block: { [weak self] in
            RunLoop.current.add(Port(), forMode: .default)
             while self?.isStop == false && (self != nil) {
                RunLoop.current.run(mode: .common, before: Date.distantFuture)
            }
            print("end-------" + Thread.current.description)
 })
thread?.start()

//线程里面真正要做的事情
    @objc func test() {
        print(#function + "------------", Thread.current)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if thread == nil{
           return
        }
        perform(#selector(test), on: thread!, with: nil, waitUntilDone: false)
    }
    
    deinit {
        print(#function)
        perform(#selector(stop), on: thread!, with: nil, waitUntilDone: true)
        thread = nil
    }
    
    //用于停止子线程的RunLoop
    @objc func stop() {
        isStop = true
        CFRunLoopStop(CFRunLoopGetCurrent())
    }

perform(#selector(stop), on: thread!, with: nil, waitUntilDone: false) 当waitUntilDone 设置为false 控制器的销毁跟子线程执行那些代码是同时进行的 就会发生坏内存访问(控制器的内存没了) 所以,要设置为true (代表子线程的代码执行完毕之后,才会继续往下走)
while self?.isStop == false && (self != nil) { RunLoop.current.run(mode: .common, before: Date.distantFuture) } 因为弱指针有可能会空

class MJThread: Thread {

    deinit {
        print(#function)
    }
}

class MJPermenant: NSObject {

    var thread: MJThread?
    var stoped = false
    typealias MJPermenantThreadTask = (()->())
    override init() {
         
        super.init()
        
        thread = MJThread(block: { [weak self] in
            RunLoop.current.add(Port(), forMode: .common)
            while (self != nil) && self?.stoped == false{
                RunLoop.current.run(mode: .default, before: Date.distantFuture)
            }
        })
       
    }
    //MARK: 开启线程
    func run() {
        thread?.start()
    }
    
    //MARK: 结束线程
    func stop() {
        
        if let innerThread = thread {
            perform(#selector(__stop), on: innerThread, with: nil, waitUntilDone: true)
        }
    }
    
    @objc fileprivate func __stop() {
        stoped = true
        CFRunLoopStop(CFRunLoopGetCurrent())
        thread = nil
    }
    
    func executeTaskWithTarget(task: @escaping MJPermenantThreadTask) {
        
        if let innerThread = thread {
            NSObject.perform(#selector(__executeTask), on: innerThread, with: task, waitUntilDone: false)
        }
    }
    
    @objc fileprivate func __executeTask(task: @escaping MJPermenantThreadTask) {
        task()
    }
    
    deinit {
        stop()
    }
}
封装的线程使用C语言的RunLoop
 // 创建上下文(要初始化一下结构体)
CFRunLoopSourceContext context = {0};
            
// 创建source
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
            
// 往Runloop中添加source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
            
// 销毁source
CFRelease(source);
            
// 启动
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);

CFRunLoopRunInMode 的 returnAfterSourceHandled,设置为true,代表执行完source后就会退出当前loop CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);

上一篇 下一篇

猜你喜欢

热点阅读