埋點
- 概念:在iOS開發(fā)中,埋點可以解決兩大類問題,一是了解用戶使用App的行為烁兰,二是降低分析線上問題的難度减响。
- 常見的埋點方式
常見的埋點方式主要包括代碼埋點靖诗、可視化埋點和無埋點三種
- 代碼埋點主要就是通過手寫代碼的形式埋點,能很精確的在需要埋點的代碼處加上埋點的代碼支示,可以很方便的記錄當前環(huán)境的變量值刊橘,方便調試,并跟蹤埋點內容颂鸿。但存在開發(fā)工作量大促绵,并且埋點代碼到處都是,后期難以維護等問題嘴纺。
- 可視化埋點败晴,就是將埋點增加和修改的工作可視化了,提升了增加和維護埋點的體驗栽渴。
- 無埋點尖坤,并不是不需要埋點,而更確切的說是“全埋點”闲擦,而且埋點代碼也不會出現(xiàn)在業(yè)務代碼中糖驴,容易管理和維護。它的缺點在于佛致,埋點成本高贮缕,后期的解析也比較復雜,再加上view_path的不確定性俺榆。所以這種方案不能解決所有的埋點需求感昼,但對于大量通用的埋點需求來說,能夠節(jié)省大量的開發(fā)和維護成本罐脊。
其中定嗓,可視化埋點和無埋點蜕琴,都屬于無侵入埋點的內容,因為它們都不需要在工程代碼中寫入埋點代碼宵溅。所以凌简,采用這樣的無侵入埋點方案,既可以做到埋點被統(tǒng)一處理恃逻,又可以實現(xiàn)和工程代碼的解耦雏搂。
運行時方法替換方式進行埋點
iOS開發(fā)中,最常見的三種埋點寇损,就是對頁面進入次數(shù)凸郑、頁面停留時間、點擊事件的埋點矛市。對于這三種常見情況芙沥,我們都可以通過運行時方法替換技術來插入埋點的代碼,以實現(xiàn)無侵入的埋點方式浊吏。
對于頁面曝光埋點而昨,則需要對VC的生命周期方法進行埋點,交換VC的viewWillAppear找田、viewDidAppear配紫、viewWillDisappear、viewDidDisappear等方法午阵,這樣的我們就可以在交互的方法里面加上埋點代碼躺孝。
如果區(qū)分不同的UIViewController呢?我們可以使用NSStringFromClass([self class])類名字符串來標識不同的UIViewController底桂。對于點擊事件來說植袍,則需要替換Button的點擊事件方法sendAction:to:forEvent,以調用埋點代碼籽懦。
和UIViewController生命周期埋點不同的是于个,UIButton在一個視圖類中可能有多個不同的繼承類,相同UIButton的子類在不同視圖類的埋點也要區(qū)分開暮顺,這時候厅篓,我們可以通過action選擇器名NSStringFromSelector(action)+視圖類名NSStringFromClass([target class])組合成為唯一的一個標識,來進行埋點記錄捶码。除了UIViewController羽氮、UIButton空間以外,Cocoa框架的其他控件都可以通過這種方法來進行無侵入埋點惫恼。以Cocoa框架中最復雜的UITableView控件為例档押,你可以hook setDelegate方法來實現(xiàn)無侵入埋點。另外,對于Cocoa框架中的手勢事件(Gesture Event)令宿,我們也可以通過hook initWithTarget:action:方法來實現(xiàn)無侵入埋點叼耙。
事件的唯一標識
通過運行時替換的方式,我們能夠hook住所有的Objective-C方法粒没,可以說大而全了筛婉,能夠幫助我們解決大部分的埋點問題。
但是癞松,這種方案的精確度還不夠高爽撒,還無法區(qū)分相同類在不同視圖樹節(jié)點的情況。比如拦惋,一個視圖下相同UIButton的不同實例,僅僅通過action選擇器名+視圖類名的組合還不能夠區(qū)分開安寺。這是厕妖,我們就需要一個唯一標識來區(qū)分不同的事件。
如何制定出這個唯一標識
- 可以通過視圖層級的路徑來解決這個問題挑庶。因為每個頁面都有一個視圖樹結構言秸,通過視圖的superview和subviews的屬性,我們就能夠還原出每個頁面的視圖樹迎捺。視圖樹的頂層是UIWindow举畸,每個視圖都在樹的子節(jié)點上。
一個視圖下的子節(jié)點可能是同一個視圖的不同實例凳枝,比如如果UIView中有兩個UIButton是同一個類的不同實例抄沮,所以光靠視圖樹的路徑還是沒辦法確定出唯一的視圖標識。
這時候岖瑰,索引可以解決這個問題:每個視圖在父視圖中都會有自己的索引叛买,如果我們再加上這個索引的話,每個視圖的標識就是唯一的了蹋订。 - 視圖層級路徑加上在父視圖中的索引來進行唯一標識率挣,也無法涵蓋所有情況
類似于UITableViewCell這種具有可復用機制的視圖,cell會在頁面滾動時不斷復用露戒,這種情況下加索引的方法還是沒用椒功。這時候我們可以通過cell的索引,也就是indexPath的section和row的值去確定cell的唯一性智什。 - UIAlertController也比較特殊动漾,它的特殊性在于視圖層級的不固定,因為它可能出現(xiàn)在任何頁面中荠锭。這時候我們可以通過上層視圖+彈窗內容來確定唯一標識谦炬。
- 其它的情況,我們可以想辦法找出相同視圖中不同的元素,然后進行組合键思,最后形成一個能夠區(qū)別于其他元素的標識來础爬。
- 除此之外,如果在程序運行過程中吼鳞,修改視圖的層級和索引看蚜,比如執(zhí)行insetSubView:atIndex:、removeFromSuperView等方法時赔桌,我們也無法獲得唯一標識供炎,即使只截取部分路徑也無法保證后期代碼更新時不會動到這個部分。就算是運行時視圖層級不會修改疾党,以后需求更新迭代頁面頻繁的話音诫,視圖唯一標識也需要同步的更新維護。
事件唯一標識的準確性難以保障雪位,這也是通過運行時方法替換進行無侵入埋點很難在各個公司全面鋪開的原因竭钝。雖然無侵入埋點無法覆蓋到所有情況,但是還是解決了部分的埋點需求雹洗,也節(jié)省了大量的人力成本香罐。
小結
通過運行時替換方法來實現(xiàn)無侵入埋點的方案,由于唯一表示難以維護和準確性難以保障的原因时肿,很難被全面采用庇茫,一般都只是用于一些功能和視圖穩(wěn)定的地方,手動埋點依然占據(jù)大部分場景螃成。
所以說旦签,這種方案不一定是未來的方向。使用clang AST的接口寸宏,在構建時遍歷AST顷霹,通過定義的規(guī)則將所需要的埋點代碼直接加進入,可能會更合適击吱。