iOS摸魚週報 第五十期

語言: CN / TW / HK

本期概要

  • 話題:WWDC 22 Call to Code
  • 面試模組:事件響應與傳遞
  • 優秀部落格:複習 iOS 的 rebase 和 bind
  • 學習資料:閒話 Swift 協程
  • 開發工具:AppleParty 是三七互娛旗下37手遊 iOS 團隊研發,實現快速操作 App Store Connect 後臺的自動化 macOS 工具。

本期話題

WWDC 22 Call to Code

Apple 宣佈了 WWDC 22 的相關事項,時間是 6 月 6 號到 10 號,形式還是線上播放。蘋果一向喜歡玩彩蛋,我們可以嘗試從這張圖片裡獲取一些資訊。圖片主體是 Swift 圖示,更準確的說應該是 SwiftUI 的圖示,圖示邊緣透出的光亮有一種黎明到來,開啟新篇章的感覺,所以很可能 SwiftUI 將迎來重大更新。就可聯想的範圍來說,什麼樣的更新才算重大呢,對標 Flutter,有沒有可能支援全棧:Windows、Linux、Web 等平臺?這個想法確實能配得上黎明到來,至於是否會實現,還是會有別的我們想不到的大更新,就讓我們等待它的到來吧!

同時 Swift Student Challenge 將繼續舉辦,學生們可以通過 Swift Playgrounds 創造有趣的專案。專案提交截止時間是 4 月 25 號,獲獎者將獲得 Apple 提供的一件 WWDC22 主題外套,一套定製的別針套裝和一年的開發者會員資格。活動詳情可以點選 Swift Student Challenge 檢視。

面試解析

整理編輯:JY

事件響應與傳遞

當指尖觸碰螢幕,觸控事件由觸屏生成後如何傳遞到當前應用?

通過 IOKit.framework 事件發生,被封裝為 IOHIDEvent物件,然後通過 mach port 轉發到 SpringBoard(也就是桌面)。然後再通過mach port轉發給當前 APP 的主執行緒,主執行緒RunloopSource1觸發,Source1回撥內部觸發Source0回撥Source0的回撥內部將事件封裝成UIEvent ,然後呼叫UIApplicationsendEventUIEvent傳給了UIWindow

souce1回撥方法: __IOHIDEventSystemClientQueueCallback()

souce0回撥方法: __UIApplicationHandleEventQueue()

尋找最佳響應者,這個過程也就是hit-testing,確定了響應鏈,接下來就是傳遞事件。

如果事件找不到能夠響應的物件,最終會釋放掉。Runloop 在事件處理完後也會睡眠等待下一次事件。

尋找事件的最佳響應者(Hit-Testing)

當 APP 接受到觸控事件後,會被放入到當前應用的一個事件佇列中(先發生先執行),出隊後,Application 首先將事件傳遞給當前應用最後顯示的UIWindow,詢問是否能夠響應事件,若視窗能夠響應事件,則向下傳遞子檢視是否能響應事件,優先詢問後新增的檢視的子檢視,如果檢視沒有能夠響應的子檢視了,則自身就是最合適的響應者。

objectivec - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { //3種狀態無法響應事件 if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil; //觸控點若不在當前檢視上則無法響應事件 if ([self pointInside:point withEvent:event] == NO) return nil; //從後往前遍歷子檢視陣列 int count = (int)self.subviews.count; for (int i = count - 1; i >= 0; i--) { // 獲取子檢視 UIView *childView = self.subviews[i]; // 座標系的轉換,把觸控點在當前檢視上座標轉換為在子檢視上的座標 CGPoint childP = [self convertPoint:point toView:childView]; //詢問子檢視層級中的最佳響應檢視 UIView *fitView = [childView hitTest:childP withEvent:event]; if (fitView) { //如果子檢視中有更合適的就返回 return fitView; } } //沒有在子檢視中找到更合適的響應檢視,那麼自身就是最合適的 return self; }

傳遞事件

找到最佳響應者後開始傳遞事件

UIApplication sendEvent =>UIWindow sendEvent =>UIWindow _sendTouchesForEvent =>touchesBegin

UIApplication 是怎麼知道要把事件傳給哪個 window 的?window 又是怎麼知道哪個檢視才是最佳響應者的呢?

hit-testing過程中將 Windowview繫結在 UIEvent上的touch物件

響應者為什麼能夠處理響應事件,提供了哪些方法?

objectivec //手指觸碰螢幕,觸控開始 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; //手指在螢幕上移動 - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; //手指離開螢幕,觸控結束 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; //觸控結束前,某個系統事件中斷了觸控,例如電話呼入 - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

觸控事件如何沿著響應鏈流動?

在確定最佳響應者之後,優先給最佳的物件響應,如果最佳物件要將事件傳遞給其他響應者,這個從底到上的過程叫做響應鏈。

如果有 UIResponder、手勢、UIControl 同時存在,是怎麼處理的?

系統提供的有預設 action 操作的 UIControl,例如 UIButton、UISwitch 等的單擊,響應優先順序比手勢高,而自定義的卻比手勢識別器要低,然後才是 UIResponder

Window 在將事件傳遞給 hit-tested view 之前,會先將事件傳遞給相關的手勢識別器,並由手勢識別器優先識別。若手勢識別器成功識別了事件,就會取消 hit-tested view對事件的響應;若手勢識別器沒能識別事件,hit-tested view 才完全接手事件的響應權。

Window怎麼知道要把事件傳遞給哪些手勢識別器?

event 繫結的touch物件維護了一個手勢陣列,在 hit-testing 的過程中收集對應的手勢識別器, Window 先將事件傳遞給這些手勢識別器,再傳給 hit-tested view。一旦有手勢識別器成功識別了手勢,Application 就會取消hit-tested view對事件的響應。

手勢識別器與UIResponder對於事件響應的聯絡?

  • Window先將綁定了觸控物件的事件傳遞給觸控物件上繫結的手勢識別器,再發送給觸控物件對應的 hit-tested view

  • 手勢識別器識別手勢期間,若觸控物件的觸控狀態發生變化,事件都是先發送給手勢識別器再發送給 hit-test view

  • 手勢識別器若成功識別了手勢,則通知 Application 取消 hit-tested view 對於事件的響應,並停止向 hit-tested view 傳送事件;

  • 若手勢識別器未能識別手勢,而此時觸控並未結束,則停止向手勢識別器傳送事件,僅向 hit-test view 傳送事件。

  • 若手勢識別器未能識別手勢,且此時觸控已經結束,則向 hit-tested view 傳送 end 狀態的 touch事件以停止對事件的響應。

cancelsTouchesInView 若設定成YES,則表示手勢識別器在識別手勢期間,截斷事件,即不會將事件傳送給hit-tested view。

delaysTouchesBegan 若設定成NO,則在手勢識別失敗時會立即通知Application傳送狀態為end的touch事件給hit-tested view以呼叫 touchesEnded:withEvent: 結束事件響應。

有哪些情況無法響應?

  • 不允許互動userInteractionEnabled = NO

  • 隱藏hidden = YES):如果父檢視隱藏,那麼子檢視也會隱藏,隱藏的檢視無法接收事件

  • 透明度:alpha < 0.01 如果設定一個檢視的透明度<0.01,會直接影響子檢視的透明度。alpha:0.0~0.01為透明。

參考

iOS觸控事件全家桶

優秀部落格

整理編輯:皮拉夫大王在此

本期優秀部落格主題為重新瞭解rebase & bind 。前段時間位元組發了篇關於iOS 15fixup-chain機制的相關文章,其中rebase機制引起了大家熱烈的討論。在討論的過程中,包括我在內的部分同學糾正了之前對rebase的錯誤認識,因此有必要跟大家一塊再來學習下rebase & bind

在閱讀之前,先來問幾個問題:

  • reabse 時會修改TEXT段的資料嗎?如果不修改,那靜態連結時還不知道ASLR後的真實地址難道不需要通過rebase修正嗎?如果要修改,TEXT段不是隻讀段嗎,為什麼可以修改呢?
  • iOS 15之前的fixup-chain機制與之前的rebase & bind有何不同?

如果你想真正瞭解rebase & bind機制,那麼這兩個問題要弄清楚。

1、複習iOS的rebase和bind

1.1 深入理解 Symbol -- 來自公眾號:小集

@皮拉夫大王:在瞭解rebase和bind之前必須要了解iOS的符號,符號是bind的橋樑。文章中對符號的介紹比較詳細,包含之前很少提到的lazy symbol,weak symbol等。

1.2 給實習生講明白 Lazy/Non-lazy Binding -- 來自掘金:No

@皮拉夫大王:這篇文章是對bind講解的淺顯易懂,非常適合之前不瞭解bind的同學閱讀。

1.3 圖解 Mach-O 中的 got -- 來自掘金:微微笑的蝸牛

@皮拉夫大王:這篇文章也是介紹相關知識的,可以補充閱讀。

2、關於iOS15的fixup機制

2.1 iOS 15 如何讓你的應用啟動更快 -- 來自掘金:ZacJi

@皮拉夫大王:iOS15的fixup介紹將主要通過三篇文章,逐次加深深度。閱讀這篇文章後,大家應該要弄清楚作者所說的啟動加速的原因,以及與二進位制重排是否有關係。

2.2 從野指標探測到對iOS 15 bind 的探索 -- 來自公眾號:皮拉夫大王在此

@皮拉夫大王:在閱讀了《iOS 15 如何讓你的應用啟動更快》,進一步探索了bind機制並且加以應用。

2.3 iOS15 動態連結 fixup chain 原理詳解 -- 來自公眾號:位元組跳動終端技術

@皮拉夫大王:更加完善地介紹iOS 15的fixup機制。

見聞

這一週閱讀/瀏覽到的有趣的資訊。

1、識花軟體-形色

@zhangferry:春天來了,又到了踏春賞花的時候,如果此時沒有疫情可能大家的腳步能走的更遠。春天的訊號最明顯的就是馬路或者公園裡盛開了各種各樣的花,五顏六色的,非常招人喜歡。當我把手機對準這些花的時候,才發現他們在我眼裡統稱為花,即使相差很大,我也很難說這是什麼花那是什麼花。後來找到了一個叫做形色的軟體,它可以通過拍照識別花的名字。我將小區和附近公園裡的花用它來識別,於是就有了下面這張圖:

終於知道這些花叫什麼名字了,當我把花和它們各自的名字對應上之後,花也感覺更好看了。

2、網際網路使用者公眾賬號資訊服務管理規定

@zhangferry:最近在公眾號讀到幾篇疫情相關的文章,轉發了一下,沒多久就被平臺刪文了。在被刪文的說明裡附了一個連結,連結指向即是《網際網路使用者公眾賬號資訊服務管理規定》,該規定的管理範圍覆蓋我們通過網路獲取的幾乎所有資訊。內容不長,分五個章節,以下僅是概括,不屬於解讀。

第一章:總則。

  • 國家網信:負責該規定的監督、執法、管理工作。
  • 公眾賬號資訊服務平臺和公眾賬號生產運營者:應當遵守法律法規,生產釋出向上向善的優質資訊內容。
  • 鼓勵各級黨政機關、企事業單位和人民團體:註冊運營公眾賬號,生產釋出高質量政務資訊或者公共服務資訊,滿足公眾資訊需求。(這也是各個社交媒體都能看到政務機關官方號的原因。)
  • 公眾賬號資訊服務平臺的運營需取得網際網路新聞資訊服務許可。

第二章:公眾賬號資訊服務平臺。(對平臺方的一些約束。) - 平臺是資訊生成內容的主題責任物件。 - 需建立公眾賬號分類註冊和分類生產製度、公眾賬號主體資訊核驗、註冊限制、保證資料真實性等措施。

第三章:公眾賬號生產運營者。(對生成資訊的一些約束。) - 如實填寫賬號註冊資訊。 - 公眾賬號生產運營者:應當履行資訊內容生產和公眾賬號運營管理主體責任。 - 遵守著作權保護相關法律法規。 - 不得釋出虛假、煽動使用者情緒、不實、違法等資訊。

第四章:監督管理。公共資訊中參與的三方都需要的監督管理。 - 平臺。 - 內容生產者。 - 各級網信部門。

第五章:附則。 - 網際網路公眾賬號覆蓋範圍,在網際網路中釋出文字、圖片、音視訊等資訊內容都包括。

生效時間:2021 年 2 月 22 日起施行。

3、什麼是CNAME以及CDN? -- 來自知乎:漢堡再來一個

@zhangferry:前一段時間發現往 Gitee 上傳圖片失敗,部落格的圖片也全掛了,開啟郵箱發現 Gitee 發的一封郵件:

果然免費的東西不好用,簡單調研之後決定遷移到七牛雲上。

使用七牛雲作為圖床的話,需要用到它的兩個服務:儲存和訪問(使用七牛雲的前提是要有備案域名)。儲存是每月免費 10 個 G。訪問的話,預設開啟 CDN 加速,這部分流量需要付費,這個可以根據需求購買對應的流量包,很便宜。這裡可以講下配置 CDN 加速時遇到的兩個概念,CNAME 和 CDN。

CDN 的作用是訪問加速,如何加速呢,就是分配多個伺服器上,就近訪問,訪問之後該伺服器會快取源站的資源,之後的訪問就不會請求源站而是直接訪問這臺就近的伺服器了。

我們配置一個域名,例如 cdn.zhangferry.com,將它的源站指向七牛的儲存空間。這裡是一對一的關係,為了能夠實現一對多,我們不直接指向源站,而是指向七牛的排程伺服器。這個實現就是利用 CNAME,它相當於給我們需要解析的域名起一個別名,訪問 cdn.zhangferry.com,就會訪問到七牛排程伺服器,這臺伺服器還可以配置 CNAME,再去指向另外一個域名。我們可以使用 dig 命令驗證這個流程,cdn 域名的最終指向是一個特定的 IP 伺服器,只不過在不同地區這個目標伺服器 IP 不同。

4、GIF:一個觀察網際網路歷史的切面 -- 來自公眾號:全媒派

@遠恆之義:GIF 動圖是數字時代的影象語言。GIF 生產方便,傳播迅速,滿足了人類交際的需求,刺激人們的視覺與情感,成了線上文化的迷因。當我們在鬥圖的時候,比的是表情包的資源,拼的是網上衝浪的時間,笑的是對“梗”文化的認同。不知道大家平時在社交媒體上使用表情包的頻率如何,有沒有想過 GIF 到底是怎麼來的呢?

5、手中有糧,心裡不慌:如何儲備自己的物資「大後方」 -- 來自少數派:喬淼

@遠恆之義:最近吉林長春和上海疫情嚴重,身邊有朋友身處這兩地被困家中,暫時性的物質短缺。看到群裡的小夥伴自己用黃豆發豆芽,心中感慨萬千,不是滋味。

這是一份科學的囤貨指南,總結了作者在物資儲備方面的心得體會,滿足短期與長期的食物儲存需求。關於其他物資的儲備以及培養個人儲備習慣,作者在文章中也有明確的建議。希望大家都能成為生活的高手。

生存危機未必只發生在遠離文明世界的地方。(各種突發事件)將切斷所有的日常服務和食物供應。……在大城市,商店的食品架上將空空如也……公園和花園裡的植物皮將會被剝光……(在正常的秩序恢復前)你只有依靠自身的資源條件和技巧安排生活。
———約翰·懷斯曼,《生存手冊》,第 11 章 14 節,「大後方」

學習資料

整理編輯:Mimosa

閒話 Swift 協程

地址:http://www.bennyhuo.com/book/swift-coroutines/

該系列部落格從淺入深地介紹了 Swift 在 5.5 中新支援的協程特性。該系列文章介紹了 Swift 協程的特性,內容以 Swift 協程的基本概念、語法設計、使用場景等方面為基礎展開,也會與大前端開發者常見的 Kotlin、JavaScript 做對比(作者是 Kotlin GDE),作者希望這個系列能給大家一個更多元化的視角來理解這個語法特性,十分推薦。

工具推薦

整理編輯:CoderStar

AppleParty

地址:http://github.com/37iOS/AppleParty

軟體狀態:開源

軟體介紹

介紹一個我們週報團隊成員所在公司開源的一個專案:AppleParty

AppleParty 是三七互娛旗下37手遊 iOS 團隊研發,實現快速操作 App Store Connect 後臺的自動化 macOS 工具。

支援功能:

  • 內購買專案管理(批量建立和更新);
  • 批量商店圖和預覽視訊上傳和更新;
  • 郵件傳送工具;
  • 二維碼掃描和生成工具;

AppleParty

關於我們

iOS 摸魚週報,主要分享開發過程中遇到的經驗教訓、優質的部落格、高質量的學習資料、實用的開發工具等。週報倉庫在這裡:http://github.com/zhangferry/iOSWeeklyLearning ,如果你有好的的內容推薦可以通過 issue 的方式進行提交。另外也可以申請成為我們的常駐編輯,一起維護這份週報。另可關注公眾號:iOS成長之路,後臺點選進群交流,聯絡我們,獲取更多內容。

往期推薦

iOS 摸魚週報 第四十九期

iOS摸魚週報 第四十八期

iOS摸魚週報 第四十七期

iOS摸魚週報 第四十六期