IOS 切面統(tǒng)計事件解耦

統(tǒng)計這個事情可以說是個巨無語的系統(tǒng)捏鱼,當(dāng)然不把他獨立出來也就不是什么問題了,只是一堆牛皮癬似得代碼穿插在項目各個地方聂渊,畢竟真正應(yīng)用到一個app里的統(tǒng)計都跟業(yè)務(wù)有著很強的綁定關(guān)系差购,脫離業(yè)務(wù)的統(tǒng)計數(shù)據(jù)基本沒什么大用,先吐槽一波再開始正文汉嗽。。找蜜。饼暑。


DingTalk20170802140347.png

基礎(chǔ)封裝

先從用第三方的來說,基本上就只是需要包個殼就ok了洗做,建個manager弓叛,初始化sdk一封裝,加幾個常用統(tǒng)計方法基本ok诚纸。常見的方法就是傳個event名再加個properties傳擴展字典撰筷,用戶登錄狀態(tài)綁定注銷,通用字段增刪改畦徘,基本上這就滿足了大部分需求毕籽。抬闯。。

如果是純自己手寫关筒,上面說的殼放著溶握,剩下的仿照sdk來,基本功能要實現(xiàn)異步隊列記錄往本地寫數(shù)據(jù)蒸播,定期上傳睡榆,處理好讀寫關(guān)系是關(guān)鍵,這里不多說不是這篇重點袍榆。

ps:有個殼才敢放開手折騰優(yōu)化

切面封裝

切面統(tǒng)計其實可以看我之前的IOS 百行代碼切面日志胀屿,整個完成的就是切面的封裝,看過的基本應(yīng)該了解這套邏輯切面的時候不關(guān)心切面方法的參數(shù)的話會非常好用包雀。
如果業(yè)務(wù)關(guān)聯(lián)強的情況碉纳,雖然也能處理但要針對那些業(yè)務(wù)作出對應(yīng)的邏輯,導(dǎo)致切面封裝里夾雜很多特殊邏輯,下面的方法內(nèi)部要對originAOP拆分取所有參馏艾,甚至要復(fù)制部分業(yè)務(wù)層邏輯過來最后完成一個統(tǒng)計劳曹。

-(void)al_logger:(id)log originAOP:(id)originAOP

所以我個人建議這里就封裝些簡單的少參甚至無參的統(tǒng)計,然后基本上簡單的通用統(tǒng)計和業(yè)務(wù)統(tǒng)計建個類或者plist琅摩,列一下要切面的類和方法就完成了铁孵。

凡事都有特例,如果本身方法內(nèi)多個參數(shù)完成一次統(tǒng)計就不多房资,那我之前的百行系列就已經(jīng)足夠你解耦了蜕劝。

ps:鄭重聲明AOP用Aspects不支持切面靜態(tài)方法,回頭有空肯定是要改的轰异,Aspects用class_replaceMethod實現(xiàn)的時候類方法沒有取isa的過程岖沛,類方法找不到,其實是可以實現(xiàn)的搭独,回頭我重寫一個關(guān)于swizzing的庫就換掉Aspects婴削。

通用統(tǒng)計

通用統(tǒng)計就是可以脫離業(yè)務(wù)完全抽離的部分,這部分其實可以設(shè)計部分業(yè)務(wù)承接牙肝,比如通用點擊事件可以把點擊的UI對象擴展出一個字典的屬性值唉俗,如此對于通用統(tǒng)計來說只是看看有沒有某個屬性有的話就扔到統(tǒng)計里,和業(yè)務(wù)層沒有關(guān)聯(lián)配椭,但后面帶來的好處很大虫溜!這個最后說。

App生命周期統(tǒng)計

應(yīng)用生命周期的統(tǒng)計隨便建個類股缸,監(jiān)聽下面的通知就ok了衡楞,當(dāng)然這個類就不能銷毀了,比較懶得做法直接AOPLogger 類擴展一下init里監(jiān)聽敦姻,因為我本身的類里連init方法都沒重寫瘾境。

UIApplicationDidFinishLaunchingNotification (通知名稱) ---> application:didFinishLaunchingWithOptions:(委托方法):在應(yīng)用程序啟動后直接進(jìn)行應(yīng)用程序級編碼的主要方式歧杏。

UIApplicationWillResignActiveNotification(通知名稱)--->applicationWillResignActive:(委托方法):用戶按下主屏幕按鈕調(diào)用 ,不要在此方法中假設(shè)將進(jìn)入后臺狀態(tài)寄雀,只是一種臨時變化得滤,最終將恢復(fù)到活動狀態(tài)

UIApplicationDidBecomActiveNotification(通知名稱) ---->applicationDidBecomeActive:(委托方法):應(yīng)用程序按下主屏幕按鈕后想要將應(yīng)用程序切換到前臺時調(diào)用,應(yīng)用程序啟動時也會調(diào)用盒犹,可以在其中添加一些應(yīng)用程序初始化代碼

UIApplicationDidEnterBackgroundNotification(通知名稱)----->applicationDidEnterBackground:(委托方法):應(yīng)用程序在此方法中釋放所有可在以后重新創(chuàng)建的資源懂更,保存所有用戶數(shù)據(jù),關(guān)閉網(wǎng)絡(luò)連接等急膀。如果需要沮协,也可以在這里請求在后臺運行更長時間。如果在這里花費了太長時間(超過5秒)卓嫂,系統(tǒng)將斷定應(yīng)用程序的行為異常并終止他慷暂。

UIApplicationWillEnterForegroundNotification(通知名稱) ---->applicationWillEnterForeground:(委托方法):當(dāng)應(yīng)用程序在applicationDidEnterBackground:花費了太長時間,終止后晨雳,應(yīng)該實現(xiàn)此方法來重新創(chuàng)建在applicationDidEnterBackground中銷毀的內(nèi)容行瑞,比如重新加載用戶數(shù)據(jù)、重新建立網(wǎng)絡(luò)連接等餐禁。

UIApplicationWllTerminateNotification(通知名稱) ----> applicationWillTerminate:(委托方法):現(xiàn)在很少使用血久,只有在應(yīng)用程序已進(jìn)入后臺,并且系統(tǒng)出于某種原因決定跳過暫停狀態(tài)并終止應(yīng)用程序時帮非,才會真正調(diào)用它氧吐。

點擊事件(不包括手勢)

這里點擊事件切面分為3類
1.UIControl的addTarget觸發(fā),UIButton都會走UIApplication里的這方法末盔,但需要忽略一部分筑舅,不然會和第二個部分重疊,而且手勢addTarget不走陨舱,不過手勢不走正好可以區(qū)分手勢

-(void)sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event;

2.設(shè)置delegate委托式系統(tǒng)點擊觸發(fā)翠拣,這里直接切了UICollectionView,UITableView隅忿,UITabBarController心剥,UITabBar,UIAlertView背桐,UIActionSheet這幾個setDelegate方法,然后加標(biāo)識判斷切委托方法蝉揍,用Aspects庫的方法swizzing因為他不允許重復(fù)swizzing算雙保險链峭。

3.UIAlertAction切block屬性的set方法,每次setBlock的時候替換成我自己的block又沾,在我的block內(nèi)執(zhí)行設(shè)置的block弊仪。

下面就是協(xié)議熙卡,用pod的話AOPLogger/AOPClick就有了,用的時候類擴展一下AOPLogger遵守協(xié)議實現(xiàn)對應(yīng)方法

@protocol AOPLoggerClickProtocol <NSObject>

@optional
- (void)alcp_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event;
- (void)alcp_customIgnore_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event;
- (void)alcp_collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath from:(id)sender;
- (void)alcp_tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath from:(id)sender;
- (void)alcp_tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController from:(id)sender;
- (void)alcp_tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item from:(id)sender;
- (void)alcp_alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex from:(id)sender;
- (void)alcp_actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex from:(id)sender;
- (void)alcp_alertControllerAction:(UIAlertAction *)action from:(id)sender;

@end

ps:覆蓋可能不全励饵,漏了什么歡迎git提pull request

手勢統(tǒng)計

這個是切了UIView的addGestureRecognizer方法驳癌,每次添加手勢的時候我多加個自己AOPLogger的taget-action到gestureRecognizer對象上,這樣手勢統(tǒng)計就搞定了役听。
如果要用這個統(tǒng)計點擊和長按颓鲜,判斷一下狀態(tài)是結(jié)束的時候統(tǒng)計gestureRecognizer類型是UITapGestureRecognizer,UILongPressGestureRecognizer就行,其實手勢的統(tǒng)計或許都在結(jié)束的時候統(tǒng)計區(qū)分下類型就夠了吧典予!

@protocol AOPLoggerGestureRecognizerProtocol <NSObject>

@optional
-(void)algrp_handleGesture:(UIGestureRecognizer*)gestureRecognizer;

@end

頁面統(tǒng)計

這個太尋常了甜滨,感覺沒什么好說,使用方法如上瘤袖,之所以還提供個pod的AOPLogger/AOPPageView,主要是如果要統(tǒng)計瀏覽時長邏輯衣摩,自己方便實現(xiàn)(如顯示的時候runtime隨便塞個date進(jìn)去,消失的時候算一下上報)捂敌,還有我默認(rèn)預(yù)先忽略了部分頁面,省了自己寫艾扮。

@protocol AOPLoggerPageViewProtocol <NSObject>

@optional
-(BOOL)alpvp_viewIgnore:(id)sender;
-(void)alpvp_viewDidAppear:(BOOL)animated sender:(id)sender;
-(void)alpvp_viewDidDisappear:(BOOL)animated sender:(id)sender;

@end

業(yè)務(wù)統(tǒng)計

業(yè)務(wù)統(tǒng)計首先放的位置要保證在對應(yīng)業(yè)務(wù)線的目錄里,畢竟跟業(yè)務(wù)關(guān)聯(lián)密切占婉,大的業(yè)務(wù)邏輯調(diào)整的時候看一下放業(yè)務(wù)統(tǒng)計目錄下對應(yīng)的地方需不需要修改泡嘴。

無參數(shù)方法統(tǒng)計

這一類就是需要寫在業(yè)務(wù)層里但統(tǒng)計的時候只記錄一個Event名,比如有個方法-(void)abc:(id)a b:(id)b c:(id)c;我只是記錄他被調(diào)用锐涯,這種直接AOPLogger調(diào)用下[AOPLogger AOPLoggerWithClassString:classString methodString:@"abc:b:c" log:@"abc"]或扔plist里默認(rèn)直接解決磕诊。

方法內(nèi)邏輯處理統(tǒng)計

這個就有點厲害了,比如下訂單的時候我買了一堆東西里面帶著各種商品屬性纹腌,要你把某種屬性的商品做統(tǒng)計霎终,只是舉例...這時候直接寫這個類的類擴展load方法里切面然后寫邏輯處理統(tǒng)計,這里不建議用Aspects庫升薯,底層保證切一個類的一個方法一次是好用的莱褒,可業(yè)務(wù)層各種奇葩邏輯都會有,想解耦經(jīng)常用奇招所以最好允許多次切面涎劈,dispatch_once保證自己的邏輯需要的切一次就可以了广凸,可以用我簡單封裝的方法。

@interface NSObject (AOPLogger)

/**
 替換或添加類方法蛛枚,即使替換過也會替換谅海,注意想單次替換使用dispatch_once保證,如果方法從未聲明過則會添加失敗
 
 @param originalSelector 原方法
 @param swizzledSelector 替換方法
 @param error 錯誤信息
 */
+(void)al_hookOrAddWithOriginClassSeletor:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector error:(NSError**)error;


/**
 替換或添加實例方法蹦浦,即使替換過也會替換扭吁,注意想單次替換使用dispatch_once保證,如果方法從未聲明過則會添加失敗

 @param originalSelector 原方法
 @param swizzledSelector 替換方法
 @param error 錯誤信息
 */
+(void)al_hookOrAddWithOriginSeletor:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector error:(NSError**)error;

@end

前后邏輯關(guān)聯(lián)統(tǒng)計

這個可以結(jié)合點擊來講,比上面還要讓人頭疼侥袜,類似統(tǒng)計我查看一個東西蝌诡,要記錄我在哪點擊的查看以及當(dāng)前頁面的數(shù)據(jù)屬性,還有從哪來到當(dāng)前頁面的枫吧,要查看東西的id浦旱。這個時候就需要結(jié)合通用統(tǒng)計來搞了,我們在最底層可以做的是把本身點擊的UI對象的默認(rèn)字典屬性和最上層頁面的默認(rèn)字典屬性以及點擊對象target上層UI對象的默認(rèn)字典屬性做統(tǒng)計九杂,最上層的激活頁面這個是可以在任何地方搞出來的颁湖,回頭我可以把類擴展放上來,在任何頁面都可以拿到當(dāng)前頁面尼酿,前一個頁面以及頁面屬于第幾層爷狈。底層在響應(yīng)默認(rèn)點擊事件之前處理我們的統(tǒng)計邏輯,接下來就是怎么把屬性賦值涎永,這個時候就需要我們?nèi)デ忻娈?dāng)前頁面里做model賦值的方法然后同時把想要統(tǒng)計的屬性賦值到當(dāng)前頁面的默認(rèn)字典屬性里鹿响,如果這是cell上的button那么賦值cell的時候就可以切面惶我。

總的來說就是尋找UI對象賦值點,然后切面為UI對象添加統(tǒng)計屬性盯蝴,我們runtime加到所有UI對象上隱藏的默認(rèn)字典屬性字段專門承接關(guān)聯(lián)信息捧挺,底層只做讀取整合闽烙,業(yè)務(wù)層切面賦值声搁。

這個時候就會發(fā)現(xiàn)數(shù)據(jù)對象與UI對象綁定協(xié)議的制定會直接決定實現(xiàn)的復(fù)雜度疏旨。

后序

業(yè)務(wù)層的統(tǒng)計我個人認(rèn)為原則上盡量后端做這個事檐涝,畢竟要保證統(tǒng)計數(shù)據(jù)的正確性霞玄,其實減少前端統(tǒng)計很重要拉岁,畢竟現(xiàn)在web(pc網(wǎng)頁)惰爬,wap(手機網(wǎng)頁)撕瞧,小程序,安卓丛版,ios一牽扯業(yè)務(wù)邏輯統(tǒng)計非常需要統(tǒng)一校準(zhǔn)页畦,不然數(shù)據(jù)絕對超乎你的想象。

歸納統(tǒng)計解耦思路

統(tǒng)計放到其他端其實解耦思路也是大致相同独令。燃箭。舍败。下面總結(jié):
1.找底層觸發(fā)的事件點
2.業(yè)務(wù)層找賦值點
3.使用AOP的思路對上面的地方進(jìn)行插入我們的統(tǒng)計邏輯

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末裙戏,一起剝皮案震驚了整個濱河市挽懦,隨后出現(xiàn)的幾起案子信柿,更是在濱河造成了極大的恐慌醒第,老刑警劉巖稠曼,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件形病,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機量瓜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绍傲,“玉大人,你說我怎么就攤上這事耍共。” “怎么了试读?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵杠纵,是天一觀的道長。 經(jīng)常有香客問我钩骇,道長,這世上最難降的妖魔是什么伊履? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任唐瀑,我火速辦了婚禮,結(jié)果婚禮上力穗,老公的妹妹穿的比我還像新娘毅弧。我一直安慰自己,他們只是感情好崖面,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布元咙。 她就那樣靜靜地躺著,像睡著了一般巫员。 火紅的嫁衣襯著肌膚如雪庶香。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天简识,我揣著相機與錄音赶掖,去河邊找鬼感猛。 笑死,一個胖子當(dāng)著我的面吹牛奢赂,可吹牛的內(nèi)容都是我干的陪白。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼呈驶,長吁一口氣:“原來是場噩夢啊……” “哼拷泽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袖瞻,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拆吆,沒想到半個月后聋迎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡枣耀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年霉晕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捞奕。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡牺堰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出颅围,到底是詐尸還是另有隱情伟葫,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布院促,位于F島的核電站筏养,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏常拓。R本人自食惡果不足惜渐溶,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弄抬。 院中可真熱鬧茎辐,春花似錦、人聲如沸掂恕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽竹海。三九已至慕蔚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斋配,已是汗流浹背孔飒。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工灌闺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坏瞄。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓桂对,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鸠匀。 傳聞我的和親對象是個殘疾皇子蕉斜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容

  • 統(tǒng)計這個事情可以說是個巨無語的系統(tǒng),當(dāng)然不把他獨立出來也就不是什么問題了缀棍,只是一堆牛皮癬似得代碼穿插在項目各個地方...
    C9090閱讀 521評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理宅此,服務(wù)發(fā)現(xiàn),斷路器爬范,智...
    卡卡羅2017閱讀 134,672評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法父腕,類相關(guān)的語法,內(nèi)部類的語法青瀑,繼承相關(guān)的語法璧亮,異常的語法,線程的語...
    子非魚_t_閱讀 31,645評論 18 399
  • 1該app解決了斥难,用戶在買海外大牌上面可以突破其它網(wǎng)購app的壟斷枝嘶!讓用戶買得起用得起大牌商品! 創(chuàng)新方法:只用大...
    353dc283cce0閱讀 1,322評論 0 0
  • 幸福是什么哑诊?幸福就是在該有的年紀(jì)群扶,做自己喜歡的事!人搭儒,到了一定年紀(jì)穷当,應(yīng)該收起自己的鋒芒,盡情享受生活帶給我們的所有...
    午后陽光的溫暖閱讀 444評論 2 3