「 iOS知識小集 」2018 · 第 20 期

知識小集發表於2018-07-10

這周公眾號釋出的以下文章:

本期知識小集的主要內容包括:

  • 一些 UI 效能優化的 tips
  • 你的專案中還用熱修復嗎?
  • Assets的幾個方便用法
  • 兩種 App 啟動連續閃退檢測策略

一些 UI 效能優化的 tips

作者: halohily

  1. 圓角效果:圓角效果的優化老生常談,產生效能問題的根源在於原生圓角效果帶來的離屏渲染開銷。通常我們推薦直接使用圓角的素材,或者提前在子執行緒將圖片進行圓角裁剪,這兩者原理相同。除此之外,還有一種思路是在需要圓角的檢視最上層新增一箇中空的圓角遮罩層,以此來做出圓角效果。這個遮罩層和被蓋在下面的檢視在顯示時會由 GPU 進行圖層混合,而圖層混合的開銷遠小於離屏渲染。值得一提的是,由於圓角效果通常在一屏中頻繁出現,所以這個遮罩的圖片素材可以只載入一次,並且應用於每一個圓角檢視,避免重複載入。

  2. 陰影效果:值得注意的是系統原生的陰影實現要求 layer 的 masksToBounds 值為 YES,所以原生的陰影效果和圓角是不相容的。高效的陰影實現是為陰影指定 shadowPath,如果你還沒用的話,不妨試一下。

  3. 適時替換輕量控制元件:@ibireme 在他的效能優化文章中提出在合適的時候用 CALayer 替換 UIView,這確實有效,不過盲目替換往往會造成程式碼維護的困難。這裡舉兩個適合的場景:繪製線條時,完全可以替換。以及靜態展示圖片時,將圖片物件賦值給 layer 的 content 屬性,也完全可以達到效果。

  4. 圖片解碼:圖片解碼的知識不再贅述,值得一提的是,對於不同的圖片格式,不同的解碼演算法,或者使用系統解碼方法時的不同引數設定,都會影響解碼效能,如果有這方面瓶頸的,不妨做多種嘗試。

再說一個經典的例子:為了實現一個簡單的畫板需求,有人會在 UIView 上頻繁呼叫 drawRect 方法進行新筆劃的繪製,殊不知有一個天生的專用圖層物件 CAShapeLayer 是很適合做這件事的。CAShapeLayer 不需要像普通 CALayer 一樣建立寄宿圖,不會造成巨量記憶體的使用,並且它使用了硬體加速。

UI 效能優化時,我們常常需要實時監測幀率。這裡講一下 @ibireme 的幀率監測工具 YYFPSLabel 的實現原理:使用 CADisplayLink,在每幀的回撥事件中,計數器 c 加一,並且累計時間間隔 t 也進行更新。當時間間隔夠 1 秒後,使用 c/t 計算出過去 1 秒的幀率,而後計數器清零,時間戳更新為當前時間戳,再重複之前步驟。因此 YYFPSLabel 的幀率更新週期在 1 秒左右。

你的專案中還用熱修復嗎?

作者: Lefe_x

前兩天知識小叢集裡有人討論關於熱修復的問題,對此我非常感興趣,今天作為一個小集和大家探討一下。雖然目前蘋果嚴禁帶有熱修復功能的 APP 上線,一旦發現,將增加稽核時間(大約是一週的時間)。蘋果主要考慮到了安全問題,避免給自己找事,所以乾脆禁用了 JSPatch。但是 JSPatch 使用的 API 並沒有違反蘋果的規定,他也就沒有一個十足的理由拒絕你的 APP 上線。這樣就導致還有很多公司在悄悄地用 JSPatch。不過原理基本都是對 JSPatch 進行混淆後使用,當然如果你有能力自己實現一個 JSPatch 也可以。

被拒蘋果的拒絕理由大概是這樣的:

「 iOS知識小集 」2018 · 第 20 期

目前我瞭解到市面上主要通過以下幾種方式進行混淆(如果對這個話題感興趣,後續我們會在【知識小集】公眾號 進一步探討):

方式一:使用官方提供的混淆方式

目前使用官方提供的 JSPatch 服務任然可以過審,據說也是通過靜態混淆-巨集定義 這中方式。

方式二:Bugly(靜態混淆-巨集定義)

Bugly 提供了熱修復功能,它提供了一種對 JSPatch 混淆的方式。在 BuglyHotfixConfuse_pch.h 檔案中把需要混淆的類名方法名替換掉。有興趣的讀者可以在這裡 檢視詳細程式碼。

「 iOS知識小集 」2018 · 第 20 期

方式三:自己混淆

自己混淆當然是最保守的,蘋果很難察覺。某天網上爆出一個 ZipArchive 安全漏洞,而這個漏洞的一個條件就是使用了類似 JSPatch 這種可以動態執行指令碼的功能,而被爆出的 APP 經查確實使用混淆後 JSPatch,而他們採用的混淆方式也就是自己混淆。所以自己混淆 JSPatch 這條路是通的。自己混淆主要是理解 JSPatch 的原理,換一種方式來實現。

Assets的幾個方便用法

作者: 高老師很忙

Assets 想必大家都使用過,今天聊幾個 Assets 比較方便的用法。

  1. 在工程中,某個通用的顏色,我們可能會用巨集或者全域性變數來表示,這樣可以方便大家的使用,但有一個弊端,在 storyboard 或者 xib 佈局的時候,設定顏色依舊要去設定具體的RGB值;而Assets給我們提供了一個很方便的功能,可以建立 New Color Set,就彌補了剛才方案的缺陷(如下圖),並且程式碼中使用也很方便。

「 iOS知識小集 」2018 · 第 20 期

「 iOS知識小集 」2018 · 第 20 期

  1. 在需要拉伸圖片的時候,通常會使用 UIImage 的 API 的-[UIImage resizableImageWithCapInsets:resizingMode:]這個方法;而 Assets 為我們提供了 Slicing 的功能(如下圖),在 Assets 中直接設定後,在 storyboard 和 xib 中就可以直接顯示拉伸後的圖片,在程式碼中使用也及其方便,直接用 -[UIImage imageNamed:] 方法即可。

「 iOS知識小集 」2018 · 第 20 期

  1. 如果是 Universal 的工程,同一個 UIImageView,在 iPhone 中顯示圖片 A,在 iPad 中顯示圖片 B,Assets 可以很方便的通過 Devices 設定,會讓程式碼看著很清爽,不會存在判斷機型再去設定圖片的噁心程式碼。在設定橫豎屏的時候也可以充分利用 Width ClassHeight Class 兩個引數(如下圖)。

「 iOS知識小集 」2018 · 第 20 期

我覺得這 3 個用法在工作中還是很實用的,當然 Assets 還有其他很好用的功能,歡迎大家一起交流。

兩種 App 啟動連續閃退檢測策略

作者: KANGZUBIN

當我們要做 App 日誌上報時,需要考慮到一種行為:App 在啟動時就崩潰閃退了,而且當遇到連續啟動閃退(也就是每次開啟 App 必崩)時,那幾乎是災難,但更可怕是,如果沒有有效的監測手段,我們可能對已發生的這種線上嚴重問題毫不知情。

WeRead 團隊部落格的[《iOS 啟動連續閃退保護方案》] (http://wereadteam.github.io/2016/05/23/GYBootingProtection/) 和 MrPeak 老師的《iOS App 連續閃退時如何上報 crash 日誌》 分別介紹了兩種簡易的如何檢測連續閃退的策略,在這裡跟大家分享一下。

  • 計時器方法

1)App 本地快取維護一個計數變數,用於表示連續閃退的次數;

2)在啟動入口方法 application:didFinishLaunchingWithOptions: 裡判斷 App 之前是否發生過連續閃退,如果有,則啟動保護流程,自我修復,日誌上報等,否則正常啟動。判斷的邏輯如下:

3)先取出快取中的啟動閃退計數 crashCount,然後把 crashCount 加 1 並儲存;

4)接著使用 dispatch_after 方法在 5s 後清零計數,如果 App 活不過 5 秒計數就不會被清零,下次啟動就可以讀取到;

5)如果發現計數變數 > maxCount,表明 App 連續 maxCount 次連續閃退,啟動保護流程,重置計數。

具體的程式碼如下圖所示:

「 iOS知識小集 」2018 · 第 20 期

這種計數器方法邏輯簡單,與原有的程式碼耦合小。但存在誤報可能(使用者在啟動 App 後又立即 kill 掉,會被誤認為是 crash),不過可以通過設定時間閾值或者在 applicationWillTerminate: 裡標記 App 是被手動 kill 來減少誤報。

  • 時間陣列比對

我們可以在本地儲存一個 App 每次啟動時間、閃退時間、手動關閉時間的時間陣列,然後在 App 啟動時根據分析各個時間戳判斷是否存在連續閃退(當閃退時間減去啟動時間小於閾值 5 秒時,則認為是啟動閃退),具體如下:

1)App 每次啟動時,記錄當前時間 launchTs,寫入時間陣列;

2)App 每次啟動時,通過 crash 採集庫,獲取上次 crash report 的時間戳 crashTs,寫入時間陣列;

3)App 在接收到 UIApplicationWillTerminateNotification 通知時,記錄當前時間戳 terminateTs,寫入時間陣列。注意,之所以要記錄 terminateTs,是為了排除一種特殊情況,即使用者啟動 App 之後立即手動 kill app。

如果我們正確記錄了上面三個時間戳,那麼我們可以得到一個與 App crash 行為相關的時間線,如下圖:

「 iOS知識小集 」2018 · 第 20 期

根據各種時間線的行為特徵,我們只需要加上時間間隔判斷,就能得知是否為連續兩次閃退了。注意,如果兩個 crashTs 之間如果存在 terminateTs,則不能被認為是連續閃退。

以上,介紹了兩種檢測 App 是否存在啟動連續閃退的策略。

此外,對於連續閃退的保護方案以及連續閃退如何上報日誌,請詳細閱讀開頭提到的兩篇博文。

關注我們

知識小集是一個團隊公眾號,主要定位在移動開發領域,分享移動開發技術,包括 iOS、Android、小程式、移動前端、React Native、weex 等。每週都會有 原創 文章分享,我們的文章都會在公眾號首發。歡迎關注檢視更多內容。

歡迎關注我們的公眾號:iOS-Tips,也歡迎加入我們的群組討論問題。可以公眾號留言 ios、flutter、web、pwa、小程式 等關鍵詞獲取入群方式。

「 iOS知識小集 」2018 · 第 20 期

相關文章