讲讲 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的运行逻辑里面进行工作的
程序中添加每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对象
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);
RunLoop休眠的实现原理
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);