雲栖号資訊:【 點選檢視更多行業資訊】
在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!
在開發過程中,埋點可以解決兩大類問題:一是了解使用者使用 App 的行為,二是降低分析線上問題的難度。目前,iOS 開發中常見的埋點方式,主要包括:
- 代碼埋點
- 可視化埋點
- 無埋點
代碼埋點主要就是通過手寫代碼的方式來埋點,能很精确的在需要埋點的代碼處加上埋點的代碼,可以很友善地記錄目前環境的變量值,友善調試,并跟蹤埋點内容,但存在開發工作量大,并且埋點代碼到處都是,後期難以維護等問題。
缺點:
- 顯而易見,你會在後期維護的時候寫的懷疑人生
- 複用性差,幾乎不能移植給其他項目
- 工作量大,而且會越寫越多
- 統計代碼上線之後,如果出現問題,隻能後續版本疊代
- 如果統計項目名字改變了,原來老的APP版本依舊會統計老的頁面名字
優點:
- 如果非要寫一個其他統計無法做到的優點的話,那就是可自定義程度高吧,統計代碼想寫到那裡寫到那裡(其實這些也可以在後面的方案實作,隻是實作上稍微麻煩一點罷了)
- 最容易想到的方案(前期費時少,使用起來費手不費思路)
就是将埋點增加和修改的工作可視化了,提升了增加和維護埋點的體驗。
該方案的具體步驟就是:
- 從背景擷取需要統計的地方
- hook住需要統計的類的load方法來Method Swizzing要統計的方法
- 上傳統計到的事件給背景分析
用UIViewController、UIControl為例子,講解一下該方案的思路。
UIViewController PV統計,頁面的統計較為簡單,利用Method Swizzing hook 系統的viewDidLoad, 直接通過頁面名稱即可鎖定頁面的展示代碼如下:

UIControl 點選統計,主要通過hook sendAction:to:forEvent: 來實作, 其唯一辨別符我們用 targetname/selector/tag來标記,具體代碼如下:
- 需要背景配合
- 可拓展性不是很高,因為需要修改背景下發的統計内容來做每次的版本統計擴充
- 相對于第一種方案,代碼量少了很多。
- 動态化從背景擷取統計内容,友善線上修改
無埋點,并不是不需要埋點,而更确切地說是“全埋點”,而且埋點代碼不會出現在業務代碼中,容易管理和維護。它的缺點在于,埋點成本高,後期的解析也比較複雜,再加上 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 了。
對于點選事件來說,我們也可以通過運作時方法替換的方式進行無侵入埋點。這裡最主要的工作是,找到這個點選事件的方法 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也許對你來說真的是最好的方案了,起碼是可以完成統計需求,雖然苦點累點。但是在合适的時間,切換不同的選擇,才是成長的展現,還是最開始的話,如果你在的團隊,已經給你了資源和時間去完善埋點這個子產品,如果你把它做的更好,那一定是一件很酷的事情。
【雲栖号線上課堂】每天都有産品技術專家分享!
課程位址:
https://yqh.aliyun.com/live立即加入社群,與專家面對面,及時了解課程最新動态!
【雲栖号線上課堂 社群】
https://c.tb.cn/F3.Z8gvnK
原文釋出時間:2020-04-30
本文作者:kvoxxx
本文來自:“
cocoachina”,了解相關資訊可以關注“cocoachina”