結合RunLoop
和實際堆棧信息解釋點擊事件的傳播(與99%的人認爲的過程不一樣)。最終結果在最後的堆棧信息圖
和手繪的事件完整傳遞圖
中。設計模式
像我這種小白開發通常都是從事件的傳遞來說的:就是UIApplication找尋最優響應者的過程(這裏就不贅述了)。bash
好吧,直接給出總結的答案:架構
簡短描述: IOKit負責響應硬件事件,Darwin內核發出Source1 <mach_port> 消息。oop
網上大多數的RunLoop基本上都是抄自這個。確實講的很好,我也讀過這個blog,可是感受根據blog裏對點擊事件的講解理解仍是有點抽象。優化
ibreime的原文中對Source0和Source1的描述以下:spa
1,Source0 只包含了一個回調(函數指針),它並不能主動觸發事件。使用時,你須要先調用 CFRunLoopSourceSignal(source),將這個 Source 標記爲待處理,而後手動調用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop。線程
2,Source1 包含了一個 mach_port 和一個回調(函數指針),被用於經過內核和其餘線程相互發送消息。這種 Source 能主動喚醒 RunLoop 的線程。設計
- touchesBegan
處打斷點-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
複製代碼
Source1和Source0均可以喚醒RunLoop,因此應該是RunLoop收到Source1直接封裝成UIEvent再分發,可是實際發現,RunLoop的堆棧調用信息中並無Source1的身影,只有Source0?3d
因而,我決定畫個圖配合堆棧信息講述下點擊事件的全過程,也幫助各位聯繫RunLoop的知識。
按照RunLoop的說法,這裏應該是Source1喚醒RunLoop纔對,可是堆棧信息中卻沒有收到Source1信息,只有Source0(UIEvent屬於Source0),
當一個硬件事件(觸摸/鎖屏/搖晃等)發生後,首先由 IOKit.framework 生成一個 IOHIDEvent 事件並由 SpringBoard 接收。這個過程的詳細狀況能夠參考這裏。SpringBoard 只接收按鍵(鎖屏/靜音等),觸摸,加速,接近傳感器等幾種 Event,隨後用 mach port 轉發給須要的App進程。隨後蘋果註冊的那個 Source1 就會觸發回調,並調用 _UIApplicationHandleEventQueue() 進行應用內部的分發。 _UIApplicationHandleEventQueue() 會把 IOHIDEvent 處理幷包裝成 UIEvent 進行處理或分發,其中包括識別 UIGesture/處理屏幕旋轉/發送給 UIWindow 等。一般事件好比 UIButton 點擊、touchesBegin/Move/End/Cancel 事件都是在這個回調中完成的。
至此一次Button的點擊事件結束,雖然RunLoop每天說,可是在實際開發中卻不經常使用,可是,其實RunLoop就跟設計模式同樣,無處不在,知識串起來以後其實對不少Bug的fix和優化會有很大啓發。
End.