RunLoop

講講 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)
    }

}

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);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自bireme,原地址:https://blog.ibireme.com/2015/05/18/runloop/...
    乜_啊_閱讀 1,682評論 0 5
  • RunLoop 的概念 一般來講,一個線程一次只能執(zhí)行一個任務(wù),執(zhí)行完成后線程就會退出。如果我們需要一個機制,讓線...
    Mirsiter_魏閱讀 678評論 0 2
  • Runloop是iOS和OSX開發(fā)中非常基礎(chǔ)的一個概念,從概念開始學(xué)習(xí)。 RunLoop的概念 -般說,一個線程一...
    小貓仔閱讀 1,113評論 0 1
  • ======================= 前言 RunLoop 是 iOS 和 OSX 開發(fā)中非?;A(chǔ)的一個...
    i憬銘閱讀 990評論 0 4
  • 轉(zhuǎn)自http://blog.ibireme.com/2015/05/18/runloop 深入理解RunLoop ...
    飄金閱讀 1,088評論 0 4

友情鏈接更多精彩內(nèi)容