1. 程式人生 > >iOS runloop的深入淺出,runloop的理解看這裡就夠了

iOS runloop的深入淺出,runloop的理解看這裡就夠了

一:什麼是runloop
事件迴圈,絕對不止是死迴圈這麼簡單的一個回答。實質上就是runloop內部狀態的轉換。1.使用者態:應用程式都是在使用者態,平時開發用到的api等都是使用者態的操作2.核心態:系統呼叫,牽涉到作業系統,底層核心相關的指令。實際上是計算機內部進行的資源排程操作。

1.等待:其實就是使用者態-核心態的轉換。事件迴圈不是while死迴圈,而是狀態轉換,切記。二:runloop的資料結構NSRunloopCFRunloop (開源)1.CFRunloop1.CFRunloopMode1.Source/Timer/Observer
1.pthead      一一對應,runloop內部一個執行緒
2.currentMode   當前mode  CFRunloopMode資料結構
  2.1 name  字串  比如NSDefaultRunloopMode  別名定義,字串名稱 去找到對應的mode  2.2 source0,source1 都是無序集合        2.2.1  source0 需要手動喚醒執行緒  非基於Port的,用於使用者主動觸發的事件    常見的source事件:比如使用者點選按鈕,拖拽,手勢等事件。source1  具備喚醒執行緒能力 通過核心和其它執行緒相互發送訊息  2.3 timers都是陣列   2.4  Observers   CFRunloopObserver觀測時間點     kCFRunloopEntry                 準備啟動runloop     KCFRunloopBeforeTimers   通知觀察者將要處理timer了     kCFRunloopBeforeSources 通知觀察者將要處理source了     kCFRunloopBeforeWaiting   將要休眠了 之後就是使用者態切換到核心態     kCFRunloopAfterWaiting     使用者態切換到核心態不久     kCFRunloopExit                   runloop退出     kCFRunloopAllActivities       觀察所有事件3.modes    NSMutableSet<CFRunloopMode *
>的集合4.commonModes  NSMutableSet<NSString *>內部的每一個字串的名稱對應了每一中Mode。 1)kCFRunLoopDefaultModeApp的預設Mode,通常主執行緒是在這個Mode下執行2)UITrackingRunLoopMode介面跟蹤 Mode,用於 ScrollView追蹤觸控滑動,保證介面滑動時不受其他Mode 影響  3)UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成後就不再使用    4)GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,通常用不到
    5)kCFRunLoopCommonModes: 這是一個佔位用的Mode,不是一種真正的Mode5.commonModeItems 裡面有多個Observer,Timer,Source三:各個資料結構的關係1.一個runloop對應了多種mode   ,每個mode下又有多種source,timer。Observer2.每次RunLoop啟動時,只能指定其中一個 Mode,這個Mode被稱作 CurrentMode 3.如果需要切換Mode,只能退出Loop,再重新指定一個Mode進入,這樣做主要是為了分隔開不同組的Source/Timer/Observer,讓其互不影響
runloop在同一時間,只能處理,一種mode下的事件1.例子解釋。。滑動tableView的時候,timer事件的輪播器不走了。 分析: 到底是什麼原因。  解決辦法:大家都知道,timer的mode從defaultmode,換到CommonMode就可以了。 問題:為什麼為什麼就可以了。2.先介紹下CommonMode
CommonMode並不是一種實際的模式,和defaultmode完全不是一回事。簡單的理解就是,如果設定了CommonMode模式,那麼runloop在切換mode的同時,也把timer的事件也帶走了,所以無論切到哪種mode下,timer的事件都是可以處理的。回答tableView滾動timer不走問題:1.預設滾動事件和timer事件都是在kCFRunloopDefaultMode下的2.此時tableView一滾動,mode切換到UITrackingRunloopMode中,上面說,runloop同一時間只能處理一種mode的事件,那麼在DefaultMode的timer就無法響應了。3.CommonMode又是一個同步多種mode的技術方案,此模式下,當runloop到UITrackingRunloopMode的時候,他把timer的事件也轉移到UITrackingRunloopMode下,那麼timer就能走了。中間穿插:GCD的定時器不受Runloop的mode的影響1.隊dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);2.建立dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,dispatchQueue);3.dispatch_source_set_timer4.dispatch_source_set_event_handler最後:你是怎麼知道CommonMode不是一種實際的模式 原始碼分析:VoidCFRunloopAddTimer(runloop,timer,commoMode) 這個方法流程:1.大致意思就是,如果是commonmodes將runloop和commonmode封裝成一個context。 2.commonModes  NSMutableSet<NSString *>內部的每一個字串的名稱對應了每一中Mode。在介紹下,這是一個集合。也就是一個set,裡面包含了所有mode下的字串。3.將set,和contex作為引數,呼叫__CFRunloopAddItemToCommonModes方法。4.這裡面我們能取到runloop具體在什麼mode下,然後再重新呼叫VoidCFRunloopAddTimer(runloop,timer,commoMode)。注意:這裡不會進行迴圈呼叫,因為此時標記的CommonMode已經變成了具體的UITrackingRunloopMode,我們將timer加到UITrackingRunloopMode,timer就能響應了。四:事件迴圈的實現機制是當你被問到,runloop具體操作流程的時候,你該怎麼回答。當我們手動點選螢幕,UI操作,實際runloop是這樣來的

那麼核心態和使用者態的轉換,實質是呼叫了mach_msg()函式。五:runloop和多執行緒之間的關係。怎麼實現一個常駐執行緒。三步走,看圖。
1.單例方法中開啟執行緒
2.執行緒執行的方法,runRequest方法,那麼我們在runRequest中新增port和source就實現了常駐執行緒。遺漏程式碼:// 標記是否要繼續時間迴圈static BOOL runAlways = YES;
我的疑惑:那麼我們用GCD開啟子執行緒的時候,runLoop是怎麼維護的呢。系統自動幫我們解決了。五:如何實現一個常駐執行緒,那麼由怎麼銷燬一個常駐執行緒。   說明:為什麼要實現常駐執行緒,因為可能有些操作,需要多次在子執行緒中執行,但是我們不想每次都開啟執行一次,結束了。。一句話,多處使用場景,讓子執行緒runloop一直存在,節省記憶體開銷。1.先懶載入一個執行緒
2.在run方法中實現runloop一直存在
第一種方式移除方法在上面addSource中加入這個CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {    switch (activity) {        case kCFRunLoopBeforeWaiting:            NSLog(@"即將進入睡眠");            // 當runloop進入空閒時,即方法執行完畢後,判斷runloop的開關,如果關閉就執行關閉操作        {            if (stop) {                NSLog(@"關閉runloop");                // 移除runloop的source                CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);                CFRelease(source);                // 沒有source的runloop是可以通過stop方法關閉的                CFRunLoopStop(CFRunLoopGetCurrent());            }        }    }}第二種方式

這種:NSPort物件在新增在runloop的port中後,引用計數會+3,然後remove後引用計數-2,莫名其妙多出來一個,導致記憶體也有洩漏的問題所以不推薦。第三種方式先說下這種的移除方式:1.self->_thread->runloop->timer(addtimer操作引用)->self(time的test事件)  迴圈引用,大環迴圈引用,記憶體洩漏。 首先要想到如何規避。2.所以此方法不建議使用,3.當然你知道怎麼解決,就用 [timer invalidate]; 用法:用當前執行緒處理事情的時候if (stop) {    NSLog(@"移除runloop的source");    [timer invalidate];} else if (doMethod) {    [self testMethod];}第四種方式
1.這種取巧模式,不知道怎麼搞。  再次使用的時候, if(stop) {  操作一番:}六、CF的記憶體管理(CoreFoundation)  1. 凡是帶有Create、Copy、Retain等字眼的函式,創建出來的物件,都需要在最後做一次release    比如CFRunLoopObserverCreate    release函式:CFRelease(物件);總結: 1.runloop怎樣做到有事做事,沒事休息。  回答:不要說執行迴圈,說是使用者態到核心態,核心態到使用者態的轉換。2.怎樣保證子執行緒資料回來更新UI的時候,不打斷使用者的滑動操作。  第一點:tableView在滑動,處於UITrackingRunloopMode模式下。  第二點:子執行緒請求的資料,那麼在和主執行緒處理的時候,我們將更新的邏輯載入defaultMode下。那麼defaultMode下的操作是不會執行的。 第三點:滑動結束了,runloop由UITrackingRunloopMode又回到defaultMode下了,那麼defaultMode下的更新操作就能執行了 、。

如果你喜歡這篇文章,或者有任何疑問,可以掃描第一個二維碼,加樓主好友哦

也可以掃第二個二維碼,關注樓主個人微信公眾號。這裡有很多生活,職業,技術相關的文章哦。歡迎您的到來。

微訊號:                                             公眾號