講講 RunLoop,項目中有用到嗎?
1.控制線程生命周期(線程?;?
2.解決NSTimer在滑動時停止工作的問題
3.監(jiān)控應(yīng)用卡頓
4.性能優(yōu)化
RunLoop內(nèi)部實現(xiàn)邏輯?
01、通知Observers:進入Loop
02、通知Observers:即將處理Timers
03、通知Observers:即將處理Sources
04、處理Blocks
05、處理Source0(可能會再次處理Blocks)
06、如果存在Source1,就跳轉(zhuǎn)到第8步
07、通知Observers:開始休眠(等待消息喚醒)
08、通知Observers:結(jié)束休眠(被某個消息喚醒)
01> 處理Timer
02> 處理GCD Async To Main Queue
03> 處理Source1
09、處理Blocks
10、根據(jù)前面的執(zhí)行結(jié)果,決定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop
RunLoop和線程的關(guān)系?
1.每條線程都有一個與之相對應(yīng)的RunLoop對象
2. RunLoop保存在一個全局的Dictionary里面 線程作為key,RunLoop作為value
3.線程剛創(chuàng)建時并沒有RunLoop對象,RunLoop會在第一次獲取它時創(chuàng)建
4. RunLoop會在線程結(jié)束的時候銷毀
5.主線程的RunLoop已經(jīng)自動獲?。▌?chuàng)建),子線程默認(rèn)沒有開啟RunLoop
timer 與 RunLoop 的關(guān)系?
- RunLoop里面有一個變量_modes 里面存放著_timers
- 如果timer設(shè)置為commonModes 會把timer放在RunLoop的變量_commonModeItems里面
- 在RunLoop的運行邏輯里面進行工作的

RunLoop的結(jié)構(gòu).png

CFRunLoopMode的結(jié)構(gòu).png
程序中添加每3秒響應(yīng)一次的NSTimer,當(dāng)拖動tableview時timer可能無法響應(yīng)要怎么解決?
創(chuàng)建 timer 把他添加到RunLoop的commonModes中
RunLoop 是怎么響應(yīng)用戶操作的, 具體流程是什么樣的?
- 由Source1把系統(tǒng)事件進行捕捉 把事件包裝成事件隊列EvenetQueue
- EvenetQueue是由Source0處理的
說說RunLoop的幾種狀態(tài)
kCFRunLoopEntry
kCFRunLoopBeforeWaiting
kCFRunLoopBeforeTimers
kCFRunLoopBeforeSources
kCFRunLoopAfterWaiting
kCFRunLoopExit
RunLoop的mode作用是什么?
1.kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默認(rèn)Mode,通常主線程是在這個Mode下運行
2.UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
RunLoop
在程序運行過程中循環(huán)做一些事情
應(yīng)用范疇
1.定時器(Timer)、PerformSelector
2.GCD Async Main Queue
3.事件響應(yīng)、手勢識別、界面刷新
4.網(wǎng)絡(luò)請求
5.AutoreleasePool
RunLoop的基本作用
- 保持程序的持續(xù)運行
- 處理App中的各種事件(比如觸摸事件、定時器事件等)
- 節(jié)省CPU資源,提高程序性能:該做事時做事,該休息時休息
RunLoop和線程的關(guān)系
1.每條線程都有一個與之相對應(yīng)的RunLoop對象
2. RunLoop保存在一個全局的Dictionary里面 線程作為key,RunLoop作為value
3.線程剛創(chuàng)建時并沒有RunLoop對象,RunLoop會在第一次獲取它時創(chuàng)建
4.RunLoop會在線程結(jié)束的時候銷毀
5.主線程的RunLoop已經(jīng)自動獲取(創(chuàng)建),子線程默認(rèn)沒有開啟RunLoop
//Foundation
[NSRunLoop currentRunLoop]; // 獲得當(dāng)前線程的RunLoop對象
[NSRunLoop mainRunLoop]; // 獲得主線程的RunLoop對象
//Core Foundation
CFRunLoopGetCurrent(); // 獲得當(dāng)前線程的RunLoop對象
CFRunLoopGetMain(); // 獲得主線程的RunLoop對象

image.png

image.png
CFRunLoopModeRef
CFRunLoopModeRef 代表RunLoop的運行模式
- 一個RunLoop包含若干個Mode 每個Mode又包含若干個Source0/Source1/Timer/Observer
- RunLoop啟動時只能選擇其中一個Mode 作為currentMode
- 如果需要切換Mode,只能退出當(dāng)前Loop,再重新選擇一個Mode進入
不同組的Source0/Source1/Timer/Observer能分隔開來,互不影響
- 如果Mode里沒有任何Source0/Source1/Timer/Observer,RunLoop會立馬退出
常見的Mode
1.kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默認(rèn)Mode,通常主線程是在這個Mode下運行
2.UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
NSRunLoopCommonModes并不是一個真的模式 他只是一個標(biāo)記
timer能在commonModes數(shù)組中存放的模式下工作
RunLoop的Mode包含的內(nèi)容
- Source0
a.觸摸事件處理
b.performSelector:onThread:
- Source1
a.基于Port的線程間通信
b.系統(tǒng)事件捕捉
- Timers
a.NSTimer
b.performSelector:withObject:afterDelay:
- Observers
a.用于監(jiān)聽RunLoop的狀態(tài)
b.UI刷新(BeforeWaiting)
c.Autorelease pool(BeforeWaiting)
CFRunLoopObserverRef
監(jiān)聽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;
}
}
//創(chuàng)建observer
//CFOptionFlags activities 監(jiān)聽什么狀態(tài)
//Boolean repeats 是否重復(fù)
//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;
}
}
//創(chuàng)建Observer
CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
});
//添加observer到RunLoop中
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
//釋放observer
CFRelease(observer);

運行邏輯.png
RunLoop休眠的實現(xiàn)原理

實現(xiàn)原理.png
while死循環(huán)還是要不斷的執(zhí)行代碼 這個時候程序沒有處于休眠狀態(tài)
實際用途
1.控制線程生命周期(線程保活)
2.解決NSTimer在滑動時停止工作的問題
3.監(jiān)控應(yīng)用卡頓
4.性能優(yōu)化
線程?;?/h5>
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)
}
}
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()
會開啟一個無限的循環(huán)且無法停止 專門用于開啟一個永不銷毀的線程(RunLoop)
會開啟一個無限的循環(huán) 無法停止
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())
}
設(shè)置一個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) 當(dāng)waitUntilDone 設(shè)置為false 控制器的銷毀跟子線程執(zhí)行那些代碼是同時進行的 就會發(fā)生壞內(nèi)存訪問(控制器的內(nèi)存沒了) 所以,要設(shè)置為true (代表子線程的代碼執(zhí)行完畢之后,才會繼續(xù)往下走)
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: 結(jié)束線程
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
// 創(chuàng)建上下文(要初始化一下結(jié)構(gòu)體)
CFRunLoopSourceContext context = {0};
// 創(chuàng)建source
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
// 往Runloop中添加source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 銷毀source
CFRelease(source);
// 啟動
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
CFRunLoopRunInMode 的 returnAfterSourceHandled,設(shè)置為true,代表執(zhí)行完source后就會退出當(dāng)前l(fā)oop CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);