麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

[ios程序啟動與運轉]-RunLoop個人小結

2019-11-14 18:20:38
字體:
來源:轉載
供稿:網友

 

學習iOS開發一般都是從UI開始的,從只知道從IB拖控件,到知道怎么在方法里寫代碼,然后會顯示什么樣的視圖,產生什么樣的事件,等等。其實程序從啟動開始,一直都是按照蘋果封裝好的代碼運行著,暴露的一些屬性和方法作為接口,是讓我們在給定的方法里寫代碼實現自定義功能,做出各種各樣的應用。這些方法的調用順序最為關鍵,熟悉了程序運轉和方法調用的順序,才可以更好地操控程序和代碼,盡量避免Xcode不報錯又實現不了功能的BUG。從Xcode的線程函數調用棧可以看到一些方法調用順序。


 
--零--從程序啟動開始到view顯示:

start---->(加載framework,動態靜態鏈接庫,啟動圖片,Info.plist,pch等)---->main函數---->UIapplicationMain函數:

  - 初始化UIApplication單例對象  - 初始化AppDelegate對象,并設為UIApplication對象的代理  - 檢查Info.plist設置的xib文件是否有效,如果有則解凍Nib文件并設置outlets,創建顯示key window、rootViewController、與rootViewController關聯的根view(沒有關聯則看rootViewController同名的xib),否則launch之后由程序員手動加載。  - 建立一個主事件循環,其中包含UIApplication的Runloop來開始處理事件。

UIApplication
  1、通過window管理視圖;
  2、發送Runloop封裝好的control消息給target;
  3、處理URL,應用圖標警告,聯網狀態,狀態欄,遠程事件等。
AppDelegate
管理UIApplication生命周期和應用的五種狀態(notRunning/inactive/active/background/suspend)。
Key Window
  1、顯示view;
  2、管理rootViewcontroller生命周期;
  3、發送UIApplication傳來的事件消息給view。
rootViewController
1、管理view(view生命周期;view的數據源/代理;view與superView之間事件響應nextResponder的“備胎”);
2、界面跳轉與傳值;
3、狀態欄,屏幕旋轉。
view
  1、通過作為CALayer的代理,管理layer的渲染(順序大概是先更新約束,再layout再display)和動畫(默認layer的屬性可動畫,view默認禁止,在UIView的block分類方法里才打開動畫)。layer是RGBA紋理,通過和mask位圖(含alpha屬性)關聯將合成后的layer紋理填充在像素點內,GPU每1/60秒將計算出的紋理display在像素點中。
  2、布局子控件(屏幕旋轉或者子視圖布局變動時,view會重新布局)。
  3、事件響應:event和guesture。
插播控制器生命周期
runloop:
  1、(要讓馬兒跑)通過do-while死循環讓程序持續運行:接收用戶輸入,調度處理事件時間。
  2、(要讓馬兒少吃草)通過mach_msg()讓runloop沒事時進入trap狀態,節省CPU資源。


  關于程序啟動原理以及各個控件的資料,已經有太多資料介紹,平時我們也經常接觸經常用到,但關于Runloop的資料,官方文檔總是太過簡練,網上資源說法也不太統一,只能從CFRunLooPRef開源代碼著手,試著學習總結下。(NSRunloop是對CFRunloopRef的面向對象封裝,但是不是線程安全)。


 

--一--Runloop

 

1、與線程和自動釋放池相關:2、CFRunLoopRef構造:數據結構;創建與退出;mode切換和item依賴;Runloop啟動         - CFRunLoopModeRef:數據結構(與CFRunLoopRef放一起了);創建;類型;           modeItems:- CFRunLoopSourceRef:數據結構(source0/source1);                             - source0 :                             - source1 :                      - CFRunLoopTimerRef:數據結構;創建與生效;相關類型(GCD的timer與CADisplayLink)                      - CFRunLoopObserverRef:數據結構;創建與添加;監聽的狀態;3、Runloop內部邏輯:關鍵在兩個判斷點(是否睡覺,是否退出)         - 代碼實現:         - 函數作用棧顯示:4、Runloop本質:mach port和mach_msg()。5、如何處理事件:         - 界面刷新:         - 手勢識別:         - GCD任務:         - timer:(與CADisplayLink)         - 網絡請求:6、應用:         - 滑動與圖片刷新;         - 常駐子線程,保持子線程一直處理事件

 

 

Runloop

1、與線程和自動釋放池相關:

Runloop的寄生于線程:一個線程只能有唯一對應的runloop;但這個根runloop里可以嵌套子runloops;
自動釋放池寄生于Runloop:程序啟動后,主線程注冊了兩個Observer監聽runloop的進出與睡覺。一個最高優先級OB監測Entry狀態;一個最低優先級OB監聽BeforeWaiting狀態和Exit狀態。
線程(創建)-->runloop將進入-->最高優先級OB創建釋放池-->runloop將睡-->最低優先級OB銷毀舊池創建新池-->runloop將退出-->最低優先級OB銷毀新池-->線程(銷毀)


2、CFRunLoopRef構造:

數據結構:

// runloop數據結構struct __CFRunLoopMode {    CFStringRef _name;            // Mode名字,     CFMutableSetRef _sources0;    // Set<CFRunLoopSourceRef>    CFMutableSetRef _sources1;    // Set<CFRunLoopSourceRef>    CFMutableArrayRef _observers; // Array<CFRunLoopObserverRef>    CFMutableArrayRef _timers;    // Array<CFRunLoopTimerRef>    ...};// mode數據結構struct __CFRunLoop {    CFMutableSetRef _commonModes;     // Set<CFStringRef>    CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>    CFRunLoopModeRef _currentMode;    // Current Runloop Mode    CFMutableSetRef _modes;           // Set<CFRunLoopModeRef>    ...};

創建與退出:mode切換和item依賴

a 主線程的runloop自動創建,子線程的runloop默認不創建(在子線程中調用NSRunLoop *runloop = [NSRunLoop currentRunLoop];獲取RunLoop對象的時候,就會創建RunLoop);b runloop退出的條件:app退出;線程關閉;設置最大時間到期;modeItem為空;c 同一時間一個runloop只能在一個mode,切換mode只能退出runloop,再重進指定mode(隔離modeItems使之互不干擾);d 一個item可以加到不同mode;一個mode被標記到commonModes里(這樣runloop不用切換mode)。

啟動Runloop:

// 用DefaultMode啟動void CFRunLoopRun(void) {    CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);}// 用指定的Mode啟動,允許設置RunLoop最大時間(假無限循環),執行完畢是否退出int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);}
  • CFRunLoopModeRef:
    數據結構(見上);
    創建添加:runloop自動創建對應的mode;mode只能添加不能刪除

    // 添加modeCFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);

類型:

1. kCFRunLoopDefaultMode: 默認 mode,通常主線程在這個 Mode 下運行。2. UITrackingRunLoopMode: 追蹤mode,保證Scrollview滑動順暢不受其他 mode 影響。3. UIInitializationRunLoopMode: 啟動程序后的過渡mode,啟動完成后就不再使用。4: GSEventReceiveRunLoopMode: Graphic相關事件的mode,通常用不到。5: kCFRunLoopCommonModes: 占位mode,作為標記DefaultMode和CommonMode用。
  • modeItems:

// 添加移除item的函數(參數:添加/移除哪個item到哪個runloop的哪個mode下)CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

A-- CFRunLoopSourceRef:事件來源

按照官方文檔CFRunLoopSourceRef為3類,但數據結構只有兩類(???)Port-Based Sources:與內核端口相關Custom Input Sources:與自定義source相關Cocoa Perform Selector Sources:與PerformSEL方法相關)

數據結構(source0/source1);

// source0 (manual): order(優先級),callout(回調函數)CFRunLoopSource {order =..., {callout =... }}// source1 (mach port):order(優先級),port:(端口), callout(回調函數)CFRunLoopSource {order = ..., {port = ..., callout =...}

source0:event事件,只含有回調,需要標記待處理(signal),然后手動將runloop喚醒(wakeup);
source1 :包含一個 mach_port 和一個回調,被用于通過內核和其他線程發送的消息,能主動喚醒runloop。

B-- CFRunLoopTimerRef:系統內“定時鬧鐘”

NSTimer和performSEL方法實際上是對CFRunloopTimerRef的封裝;runloop啟動時設置的最大超時時間實際上是GCD的dispatch_source_t類型。

數據結構:

// Timer:interval:(鬧鐘間隔), tolerance:(延期時間容忍度),callout(回調函數)CFRunLoopTimer {firing =..., interval = ...,tolerance = ...,next fire date = ...,callout = ...}

創建與生效;

//NSTimer:  // 創建一個定時器(需要手動加到runloop的mode中) + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;  // 默認已經添加到主線程的runLoop的DefaultMode中  + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;// performSEL方法// 內部會創建一個Timer到當前線程的runloop中(如果當前線程沒runloop則方法無效;performSelector:onThread: 方法放到指定線程runloop中)- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay

相關類型(GCD的timer與CADisplayLink)

GCD的timer:
dispatch_source_t 類型,可以精確的參數,不用以來runloop和mode,性能消耗更小。

dispatch_source_set_timer(dispatch_source_t source, // 定時器對象                              dispatch_time_t start, // 定時器開始執行的時間                              uint64_t interval, // 定時器的間隔時間                              uint64_t leeway // 定時器的精度                              );

CADisplayLink :
Timer的tolerance表示最大延期時間,如果因為阻塞錯過了這個時間精度,這個時間點的回調也會跳過去,不會延后執行。
CADisplayLink 是一個和屏幕刷新率一致的定時器,如果在兩次屏幕刷新之間執行了一個長任務,那其中就會有一幀被跳過去(和 NSTimer 相似,只是沒有tolerance容忍時間),造成界面卡頓的感覺。

C--CFRunLoopObserverRef:監聽runloop狀態,接收回調信息(常見于自動釋放池創建銷毀)

數據結構:

// Observer:order(優先級),ativity(監聽狀態),callout(回調函數)CFRunLoopObserver {order = ..., activities = ..., callout = ...}

創建與添加;

// 第一個參數用于分配該observer對象的內存空間// 第二個參數用以設置該observer監聽什么狀態// 第三個參數用于標識該observer是在第一次進入run loop時執行還是每次進入run loop處理時均執行// 第四個參數用于設置該observer的優先級,一般為0// 第五個參數用于設置該observer的回調函數// 第六個參數observer的運行狀態   CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {      // 執行代碼}

監聽的狀態;

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {    kCFRunLoopEntry         = (1UL << 0), // 即將進入Loop    kCFRunLoopBeforeTimers  = (1UL << 1), // 即將處理 Timer    kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source    kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進入休眠    kCFRunLoopAfterWaiting  = (1UL << 6), // 剛從休眠中喚醒    kCFRunLoopExit          = (1UL << 7), // 即將退出Loop};

3、Runloop內部邏輯:關鍵在兩個判斷點(是否睡覺,是否退出)

  • 代碼實現:

// RunLoop的實現int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {    // 0.1 根據modeName找到對應mode    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false);    // 0.2 如果mode里沒有source/timer/observer, 直接返回。    if (__CFRunLoopModeIsEmpty(currentMode)) return;    // 1.1 通知 Observers: RunLoop 即將進入 loop。---(OB會創建釋放池)    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);    // 1.2 內部函數,進入loop    __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {        Boolean sourceHandledThisLoop = NO;        int retVal = 0;        do {            // 2.1 通知 Observers: RunLoop 即將觸發 Timer 回調。            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);            // 2.2 通知 Observers: RunLoop 即將觸發 Source0 (非port) 回調。            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);            // 執行被加入的block            __CFRunLoopDoBlocks(runloop, currentMode);            // 2.3 RunLoop 觸發 Source0 (非port) 回調。            sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);            // 執行被加入的block            __CFRunLoopDoBlocks(runloop, currentMode);            // 2.4 如果有 Source1 (基于port) 處于 ready 狀態,直接處理這個 Source1 然后跳轉去處理消息。            if (__Source0DidDispatchPortLastTime) {                Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)                if (hasMsg) goto handle_msg;            }            // 3.1 如果沒有待處理消息,通知 Observers: RunLoop 的線程即將進入休眠(sleep)。--- (OB會銷毀釋放池并建立新釋放池)            if (!sourceHandledThisLoop) {                __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);            }            // 3.2. 調用 mach_msg 等待接受 mach_port 的消息。線程將進入休眠, 直到被下面某一個事件喚醒。            // -  一個基于 port 的Source1 的事件。            // -  一個 Timer 到時間了            // -  RunLoop 啟動時設置的最大超時時間到了            // -  被手動喚醒            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {                mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg            }            // 3.3. 被喚醒,通知 Observers: RunLoop 的線程剛剛被喚醒了。            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);            // 4.0 處理消息。            handle_msg:            // 4.1 如果消息是Timer類型,觸發這個Timer的回調。            if (msg_is_timer) {                __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())            }             // 4.2 如果消息是dispatch到main_queue的block,執行block。            else if (msg_is_dispatch) {                __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);            }             // 4.3 如果消息是Source1類型,處理這個事件            else {                CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);                sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);                if (sourceHandledThisLoop) {                    mach_msg(reply, MACH_SEND_MSG, reply);                }            }            // 執行加入到Loop的block            __CFRunLoopDoBlocks(runloop, currentMode);            // 5.1 如果處理事件完畢,啟動Runloop時設置參數為一次性執行,設置while參數退出Runloop            if (sourceHandledThisLoop && stopAfterHandle) {                retVal = kCFRunLoopRunHandledSource;            // 5.2 如果啟動Runloop時設置的最大運轉時間到期,設置while參數退出Runloop            } else if (timeout) {                retVal = kCFRunLoopRunTimedOut;            // 5.3 如果啟動Runloop被外部調用強制停止,設置while參數退出Runloop            } else if (__CFRunLoopIsStopped(runloop)) {                retVal = kCFRunLoopRunStopped;            // 5.4 如果啟動Runloop的modeItems為空,設置while參數退出Runloop            } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {                retVal = kCFRunLoopRunFinished;            }            // 5.5 如果沒超時,mode里沒空,loop也沒被停止,那繼續loop,回到第2步循環。        } while (retVal == 0);    }    // 6. 如果第6步判斷后loop退出,通知 Observers: RunLoop 退出。--- (OB會銷毀新釋放池)    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);}
  • 函數作用棧顯示:

{    // 1.1 通知Observers,即將進入RunLoop    // 此處有Observer會創建AutoreleasePool: _objc_autoreleasePoolPush();    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);    do {        // 2.1 通知 Observers: 即將觸發 Timer 回調。        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);        // 2.2 通知 Observers: 即將觸發 Source (非基于port的,Source0) 回調。        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);         // 執行Block        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);        // 2.3 觸發 Source0 (非基于port的) 回調。        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);        // 執行Block        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);        // 3.1 通知Observers,即將進入休眠        // 此處有Observer釋放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);        // 3.2 sleep to wait msg.        mach_msg() -> mach_msg_trap();        // 3.3 通知Observers,線程被喚醒        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);        // 4.1 如果是被Timer喚醒的,回調Timer        __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);        // 4.2 如果是被dispatch喚醒的,執行所有調用 dispatch_async 等方法放入main queue 的 block        __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);        // 4.3 如果如果Runloop是被 Source1 (基于port的) 的事件喚醒了,處理這個事件        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);        // 5. 退出判斷函數調用棧無顯示    } while (...);    // 6. 通知Observers,即將退出RunLoop    // 此處有Observer釋放AutoreleasePool: _objc_autoreleasePoolPop();    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);}

一步一步寫具體的實現邏輯過于繁瑣不便理解,按Runloop狀態大致分為:

1- Entry:通知OB(創建pool);2- 執行階段:按順序通知OB并執行timer,source0;若有source1執行source1;3- 休眠階段:利用mach_msg判斷進入休眠,通知OB(pool的銷毀重建);被消息喚醒通知OB;4- 執行階段:按消息類型處理事件;5- 判斷退出條件:如果符合退出條件(一次性執行,超時,強制停止,modeItem為空)則退出,否則回到第2階段;6- Exit:通知OB(銷毀pool)。

4、Runloop本質:mach port和mach_msg()。

Mach是XNU的內核,進程、線程和虛擬內存等對象通過端口發消息進行通信,Runloop通過mach_msg()函數發送消息,如果沒有port 消息,內核會將線程置于等待狀態 mach_msg_trap() 。如果有消息,判斷消息類型處理事件,并通過modeItem的callback回調(處理事件的具體執行是在DoBlock里還是在回調里目前我還不太明白???)。

Runloop有兩個關鍵判斷點,一個是通過msg決定Runloop是否等待,一個是通過判斷退出條件來決定Runloop是否循環。


5、如何處理事件:

  • 界面刷新:
    當UI改變( Frame變化、 UIView/CALayer 的繼承結構變化等)時,或手動調用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,這個 UIView/CALayer 就被標記為待處理。
    蘋果注冊了一個用來監聽BeforeWaiting和Exit的Observer,在它的回調函數里會遍歷所有待處理的 UIView/CAlayer 以執行實際的繪制和調整,并更新 UI 界面。

  • 事件響應:
    當一個硬件事件(觸摸/鎖屏/搖晃/加速等)發生后,首先由 IOKit.framework 生成一個 IOHIDEvent 事件并由 SpringBoard 接收, 隨后由mach port 轉發給需要的App進程。
    蘋果注冊了一個 Source1 (基于 mach port 的) 來接收系統事件,通過回調函數觸發Sourece0(所以UIEvent實際上是基于Source0的),調用 _UIApplicationHandleEventQueue() 進行應用內部的分發。
    _UIApplicationHandleEventQueue() 會把 IOHIDEvent 處理并包裝成 UIEvent 進行處理或分發,其中包括識別 UIGesture/處理屏幕旋轉/發送給 UIWindow 等。

  • 手勢識別:
    如果上一步的 _UIApplicationHandleEventQueue() 識別到是一個guesture手勢,會調用Cancel方法將當前的touchesBegin/Move/End 系列回調打斷。隨后系統將對應的 UIGestureRecognizer 標記為待處理。
    蘋果注冊了一個 Observer 監測 BeforeWaiting (Loop即將進入休眠) 事件,其回調函數為 _UIGestureRecognizerUpdateObserver(),其內部會獲取所有剛被標記為待處理的 GestureRecognizer,并執行GestureRecognizer的回調。
    當有 UIGestureRecognizer 的變化(創建/銷毀/狀態改變)時,這個回調都會進行相應處理。

  • GCD任務:
    當調用 dispatch_async(dispatch_get_main_queue(), block) 時,libDispatch 會向主線程的 RunLoop 發送消息,RunLoop會被喚醒,并從消息中取得這個 block,并在回調里執行這個 block。Runloop只處理主線程的block,dispatch 到其他線程仍然是由 libDispatch 處理的。

  • timer:(見上modeItem部分)

  • 網絡請求:
    關于網絡請求的接口:最底層是Cfsocket層,然后是CFNetwork將其封裝,然后是NSURLConnection對CFNetwork進行面向對象的封裝,NSURLsession 是 iOS7 中新增的接口,也用到NSURLConnection的loader線程。所以還是以NSURLConnection為例。
    當開始網絡傳輸時,NSURLConnection 創建了兩個新線程:com.apple.NSURLConnectionLoader 和 com.apple.CFSocket.private。其中 CFSocket 線程是處理底層 socket 連接的。NSURLConnectionLoader 這個線程內部會使用 RunLoop 來接收底層 socket 的事件,并通過之前添加的 Source0 通知到上層的 Delegate。


6、應用:

  • 滑動與圖片刷新;
    當tableview的cell上有需要從網絡獲取的圖片的時候,滾動tableView,異步線程會去加載圖片,加載完成后主線程就會設置cell的圖片,但是會造成卡頓??梢宰屧O置圖片的任務在CFRunLoopDefaultMode下進行,當滾動tableView的時候,RunLoop是在 UITrackingRunLoopMode 下進行,不去設置圖片,而是當停止的時候,再去設置圖片。

- (void)viewDidLoad {  [super viewDidLoad];  // 只在NSDefaultRunLoopMode下執行(刷新圖片)  [self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:ti inModes:@[NSDefaultRunLoopMode]];    }
  • 常駐子線程,保持子線程一直處理事件
    為了保證線程長期運轉,可以在子線程中加入RunLoop,并且給Runloop設置item,防止Runloop自動退出。

+ (void)networkRequestThreadEntryPoint:(id)__unused object {    @autoreleasepool {        [[NSThread currentThread] setName:@"AFNetworking"];        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];        [runLoop run];    }}+ (NSThread *)networkRequestThread {    static NSThread *_networkRequestThread = nil;    static dispatch_once_t oncePredicate;    dispatch_once(&oncePredicate, ^{        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];        [_networkRequestThread start];    });    return _networkRequestThread;}- (void)start {    [self.lock lock];    if ([self isCancelled]) {        [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];    } else if ([self isReady]) {        self.state = AFOperationExecutingState;        [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];     [self.lock unlock];
}   }

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 亚洲成人免费视频在线 | 亚洲精品日韩色噜噜久久五月 | 日日草天天干 | 91网站免费观看 | 日韩精品久久久久久久电影99爱 | 钻石午夜影院 | 91久久国产露脸精品国产护士 | 91久久国产露脸精品国产护士 | 黄色av网站在线观看 | 精品久久久久久久久亚洲 | 72pao成人国产永久免费视频 | 日本一区二区在线 | 91成人免费在线观看 | 超碰人人做人人爱 | 国产精品欧美久久久久一区二区 | 日本黄色免费片 | 久久精品日产高清版的功能介绍 | 久久久久久久.comav | 精品国产乱码久久久久久丨区2区 | 国产成人精品区一区二区不卡 | 久久人人做 | 免费一级高清毛片 | 国产精品成人久久 | 伊久在线 | 国产一区二区三区高清 | 护士xxxx | 成人午夜天堂 | 久久久久女人精品毛片九一 | 污片视频在线观看 | 凹凸成人精品亚洲精品密奴 | 国产三级精品最新在线 | 久久久国产电影 | 奶子吧naiziba.cc免费午夜片在线观看 | 成人福利网 | 日日噜噜噜噜久久久精品毛片 | 久久99精品久久久久久秒播放器 | 羞羞网站在线看 | 97伦理 | 草久网 | 欧美视频一区二区三区 | 一本色道久久综合亚洲精品图片 |