iOS無侵入的埋點方案如何實現?

来源:https://www.cnblogs.com/chengxyyh/archive/2020/06/13/13115813.html
-Advertisement-
Play Games

在開發過程中,埋點可以解決兩大類問題:一是瞭解用戶使用 App 的行為,二是降低分析線上問題的難度。目前,iOS 開發中常見的埋點方式,主要包括: 代碼埋點 可視化埋點 無埋點 代碼埋點 代碼埋點主要就是通過手寫代碼的方式來埋點,能很精確的在需要埋點的代碼處加上埋點的代碼,可以很方便地記錄當前環境的 ...


在開發過程中,埋點可以解決兩大類問題:一是瞭解用戶使用 App 的行為,二是降低分析線上問題的難度。目前,iOS 開發中常見的埋點方式,主要包括:

  • 代碼埋點
  • 可視化埋點
  • 無埋點

代碼埋點

代碼埋點主要就是通過手寫代碼的方式來埋點,能很精確的在需要埋點的代碼處加上埋點的代碼,可以很方便地記錄當前環境的變數值,方便調試,並跟蹤埋點內容,但存在開發工作量大,並且埋點代碼到處都是,後期難以維護等問題。

缺點:

  1. 顯而易見,你會在後期維護的時候寫的懷疑人生
  2. 復用性差,幾乎不能移植給其他項目
  3. 工作量大,而且會越寫越多
  4. 統計代碼上線之後,如果出現問題,只能後續版本迭代
  5. 如果統計項目名字改變了,原來老的APP版本依舊會統計老的頁面名字

優點:

  1. 如果非要寫一個其他統計無法做到的優點的話,那就是可自定義程度高吧,統計代碼想寫到那裡寫到那裡(其實這些也可以在後面的方案實現,只是實現上稍微麻煩一點罷了)
  2. 最容易想到的方案(前期費時少,使用起來費手不費思路)

可視化埋點

就是將埋點增加和修改的工作可視化了,提升了增加和維護埋點的體驗。

該方案的具體步驟就是:

  1. 從後臺獲取需要統計的地方
  2. hook住需要統計的類的load方法來Method Swizzing要統計的方法
  3. 上傳統計到的事件給後臺分析

用UIViewController、UIControl為例子,講解一下該方案的思路。

UIViewController PV統計,頁面的統計較為簡單,利用Method Swizzing hook 系統的viewDidLoad, 直接通過頁面名稱即可鎖定頁面的展示代碼如下:

UIControl 點擊統計,主要通過hook sendAction:to:forEvent: 來實現, 其唯一標識符我們用 targetname/selector/tag來標記,具體代碼如下:

缺點:

  1. 需要後臺配合
  2. 可拓展性不是很高,因為需要修改後臺下發的統計內容來做每次的版本統計擴展

優點:

  1. 相對於第一種方案,代碼量少了很多。
  2. 動態化從後臺獲取統計內容,方便線上修改

無埋點

無埋點,並不是不需要埋點,而更確切地說是“全埋點”,而且埋點代碼不會出現在業務代碼中,容易管理和維護。它的缺點在於,埋點成本高,後期的解析也比較複雜,再加上 view_path 的不確定性。所以,這種方案並不能解決所有的埋點需求,但對於大量通用的埋點需求來說,能夠節省大量的開發和維護成本。

在這其中,可視化埋點和無埋點,都屬於是無侵入的埋點方案,因為它們都不需要在工程代碼中寫入埋點代碼。所以,採用這樣的無侵入埋點方案,既可以做到埋點被統一維護,又可以實現和工程代碼的解耦。

接下來,我們就通過今天這篇文章,一起來分析一下無侵入埋點方案的實現問題吧。

運行時方法替換方式進行埋點

我們都知道,在 iOS 開發中最常見的三種埋點,就是對頁面進入次數、頁面停留時間、點擊事件的埋點。對於這三種常見情況,我們都可以通過運行時方法替換技術來插入埋點代碼,以實現無侵入的埋點方法。具體的實現方法是:先寫一個運行時方法替換的類 ViewHook,加上替換的方法 hookClass:fromSelector:toSelector,代碼如下:

這個方法利用運行時method_exchangeImplementations 介面將方法的實現進行了交換,原方法調用時就會被hook 住,從而去執行指定的方法。

頁面進入次數、頁面停留時間都需要對 UIViewController 生命周期進行埋點,你可以創建一個 UIViewController 的 Category,代碼如下:

可以看到,Category 在+load() 方法里使用了 ViewHook 進行方法替換,在替換的方法里執行需要埋點的方法 [self insertToViewWillAppear]。 這樣的話,每個UIViewController生命周期到了ViewWillAppear 時都會去執行insertToViewWillAppear 方法。

那麼,我們要怎麼區別不同的 UIViewController 呢?我一般採取的做法都是,使用NSStringFromClass([self class]) 方法來取類名。這樣,我就能夠通過類名來區別不同的 UIViewController 了。

作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交
流群:519832104 不管你是小白還是大牛歡迎入駐,分享經驗,討論技術,大家一起交流學習成長!

另附上一份各好友收集的大廠面試題,需要iOS開發學習資料、面試真題,可以添加
iOS開發進階交流群,進群可自行下載!

對於點擊事件來說,我們也可以通過運行時方法替換的方式進行無侵入埋點。這裡最主要的工作是,找到這個點擊事件的方法 sendAction:to:forEvent:,然後在 +load() 方法使用 ViewHook 替換成為你定義的方法。完整代碼實現如下:

和 UIViewController 生命周期埋點不同的是,UIButton 在一個視圖類中可能有多個不同的繼承類,相同 UIButton 的子類在不同視圖類的埋點也要區別開。所以,我們需要通過 “action 選擇器名NSStringFromSelector(action)” +“視圖類名 NSStringFromClass([target class])”組合成一個唯一的標識,來進行埋點記錄。

除了 UIViewController、UIButton 控制項以外,Cocoa 框架的其他控制項都可以使用這種方法來進行無侵入埋點。以 Cocoa 框架中最複雜的 UITableView 控制項為例,你可以使用 hook setDelegate 方法來實現無侵入埋點。另外,對於 Cocoa 框架中的手勢事件(Gesture Event),我們也可以通過 hook initWithTarget:action: 方法來實現無侵入埋點。

事件唯一標識

通過運行時方法替換的方式,我們能夠 hook 住所有的 Objective-C 方法,可以說是大而全了,能夠幫助我們解決絕大部分的埋點問題。

但是,這種方案的精確度還不夠高,還無法區分相同類在不同視圖樹節點的情況。比如,一個視圖下相同 UIButton 的不同實例,僅僅通過 “action 選擇器名”+“視圖類名”的組合還不能夠區分開。這時,我們就需要有一個唯一標識來區分不同的事件。接下來,我就跟你說說如何制定出這個唯一標識。

這時,我首先想到的就是,能不能通過視圖層級的路徑來解決這個問題。因為每個頁面都有一個視圖樹結構,通過視圖的 superview 和 subviews 的屬性,我們就能夠還原出每個頁面的視圖樹。視圖樹的頂層是 UIWindow,每個視圖都在樹的子節點上。如下圖所示:

一個視圖下的子節點可能是同一個視圖的不同實例,比如上圖中 UIView 視圖節點下的兩個 UIButton 是同一個類的不同實例,所以光靠視圖樹的路徑還是沒法唯一確定出視圖的標識。那麼,這種情況下,我們又應該如何區別不同的視圖呢?

這時,我們想到了索引:每個子視圖在父視圖中都會有自己的索引,所以如果我們再加上這個索引的話,每個視圖的標識就是唯一的了。

接下來的一個問題是,視圖層級路徑加上在父視圖中的索引來進行唯一標識,是不是就能夠涵蓋所有情況了呢?

當然不是。我們還需要考慮類似 UITableViewCell 這種具有可復用機制的視圖,Cell 會在頁面滾動時不斷復用,所以加索引的方式還是沒法用。

但這個問題也並不是無解的。UITableViewCell 需要使用 indexPath,這個值里包含了 section 和 row 的值。所以,我們可以通過 indexPath 來確定每個 Cell 的唯一性。

除了 UITableViewCell 這種情況之外, UIAlertController 也比較特殊。它的特殊性在於視圖層級的不固定,因為它可能出現在任何頁面中。但是,我們都知道它的功能區分往往通過彈窗內容來決定,所以可以通過內容來確定它的唯一標識。

除此之外,還有更多需要特殊處理的情況,但我們總是可以通過一些辦法去確定它們的唯一性,所以我在這裡也就不再一一列舉了。思路上來說就是,想辦法找出元素間不相同的因素然後進行組合,最後形成一個能夠區別於其他元素的標識來。

除了上面提到的這些特殊情況外,還有一種情況使得我們也難以得到準確的唯一標識。如果視圖層級在運行時會被更改,比如執行 insertSubView:atIndex:、removeFromSuperView 等方法時,我們也無法得到唯一標識,即使只截取部分路徑也無法保證後期代碼更新時不會動到這個部分。就算是運行時視圖層級不會修改,以後需求迭代頁面更新頻繁的話,視圖唯一標識也需要同步的更新維護。

這種問題就不好解決了,事件唯一標識的準確性難以保障,這也是通過運行時方法替換進行無侵入埋點很難在各個公司全面鋪開的原因。雖然無侵入埋點無法覆蓋到所有情況,全面鋪開面臨挑戰,但是無侵入埋點還是解決了大部分的埋點需求,也節省了大量的人力成本。

最好的方案永遠是針對於不同的場景來說的,我們不可能在一個創業團隊一開始就選擇方案3的架構,所以對於你來說,你要自己抉擇目前而言對你最好的方案,如果你沒有後臺業務同學的支持,方案1也許對你來說真的是最好的方案了,起碼是可以完成統計需求,雖然苦點累點。但是在合適的時間,切換不同的選擇,才是成長的體現,還是最開始的話,如果你在的團隊,已經給你了資源和時間去完善埋點這個模塊,如果你把它做的更好,那一定是一件很酷的事情。

點擊此處,立即與iOS大牛交流學習


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 該文章,GitHub已收錄,歡迎老闆們前來Star! GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual 資料庫範式 一、什麼是資料庫範式 設計關係資料庫時,遵從不同的規範要求,設計出合理的關係型資料庫,這些不同的規範要求被稱為不同的 ...
  • 代碼潔癖狂們!看到一個類中有幾十個if-else是不是很抓狂? 設計模式學了用不上嗎?面試的時候問你,你只能回答最簡單的單例模式,問你有沒有用過反射之類的高級特性,回答也是否嗎? 這次就讓設計模式(模板方法模式+工廠模式)和反射助你消滅if-else! 真的是開發中超超超超超超有用的乾貨啊! 那個坑 ...
  • redis 的集合是無序的,集合成員是唯一的,不能重覆。用戶可以快速地對集合執行添加元素操作、移除元素操作以及檢查一個元素是否存在於集合中。這裡介紹一些常用的集合處理命令,併在 Yii 中的使用。 SADD SADD:SADD key-name item [item …]將一個或多個元素添加到集合里 ...
  • 如何查看SQL執行計劃 使用 PL/SQL 查看,具體使用方法如下: 新建 解釋計劃視窗 ,將 SQL 複製進去執行,即可顯示執行計劃。 選中 SQL 語句,點擊菜單 工具-解釋計劃 或 按快捷鍵 F5 執行計劃結果說明 表掃描 table access by index rowid 通過ROWID ...
  • 現在有如下表 id name age 1 張三 23 2 李四 34 3 張三 23 4 李四 32 需求 : 按照name和age欄位聯合去重 sql如下 select * from user group by name,age 文章轉自:https://blog.csdn.net/qq_2898 ...
  • 1.重置密碼的第一步就是跳過MySQL的密碼認證過程,方法如下: #vim /etc/my.cnf(註:windows下修改的是my.ini) 很多老鐵,在開始時設置了 MySQL 的密碼,後來一段時間沒有用 MySQL之後,密碼忘了~ QAQ,請別急,現在有以下方法解決密碼忘了的情況。 1.首先我 ...
  • 最近一個電子看板小項目上線,由於資料庫非常小,而且數據也不太重要。因此未選擇XtraBackup備份,打算用AutoMySQLBackup來備份,結果部署後測試發現,有一些小問題是之前解決過的。有一些是MySQL 5.7版本才有的。下麵記錄一下解決過程。關於AutoMySQLBackup的基礎知識,... ...
  • 42.統計APP應用的DB連接及IP情況 select b.hostname ,a.client_net_address, b.program_name ,count(1) as Qtyfrom sys.dm_exec_connections a(nolock) inner join sys.sys ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...