iOS 使用AOP統(tǒng)計打點

統(tǒng)計打點是 App 開發(fā)里很重要的一個環(huán)節(jié)藻肄,App 的運行狀態(tài)潮峦、用戶的各種行為等都需要打點或舞,有不少關(guān)于統(tǒng)計的第三方庫(如友盟統(tǒng)計)兴喂。但是蔼囊,如果要求在整個項目的所有button里統(tǒng)計用戶的點擊事件,假如一個項目里面有1000個button衣迷,你就要設(shè)1000個地方設(shè)置統(tǒng)計代碼畏鼓,顯然不科學(xué)。
有沒有一種辦法在一個地方進行統(tǒng)計打點壶谒,檢測整個應(yīng)用的點擊事件云矫?

方案一:使用Runtime的方式追蹤點擊的按鈕

特點:需要對每個button進行tag編號,對手勢點擊汗菜、tableView的點擊要單獨配置让禀,比較繁瑣

方案二:使用面向切面編程AOP對按鈕或者頁面進行追蹤(無需在任何詳情頁面中做相應(yīng)配置)

特點:

1挑社、在不修改源代碼的情況下,通過運行時給程序添加統(tǒng)一功能的技術(shù)巡揍,可以用作日志記錄痛阻,性能統(tǒng)計等
2、無需對每個button進行tag編號腮敌,創(chuàng)建button后只需在新建的plist中配置button對應(yīng)的方法名和對應(yīng)的事件 ID就行
3录平、適用于Tap點擊手勢,使用時設(shè)置事件ID缀皱,和button的使用方法一樣
4斗这、button不支持直接在block里面寫事件的方式,但可以在block里面調(diào)用方法或者需要統(tǒng)一寫成下面的方式

[button addTarget:self  action:@selector(click)forControlEvents:UIControlEventTouchUpInside];

5啤斗、適用于tableview的didSelectRowAtIndexPath點擊事件表箭,可獲取tableView對應(yīng)的類名、section和row
6钮莲、如果是統(tǒng)計tableview的點擊事件免钻,根據(jù)需要在獲取到section和row后加個判斷埋點統(tǒng)計

if (section == 0 && row == 1) {
   [MobClick event:eventID];
}

7、如果有特殊需求:某個按鈕登錄前和登錄后記錄的事件不一樣崔拥,需要加判斷

if ([eventID isEqualToString:@"xxx"]) {
    [EJServiceUserInfo isLogin]?[MobClick event:eventID]:[MobClick event:@"???"];
   }else{
   [MobClick event:eventID];
 }

以下是具體代碼:

(不得不吐槽一下极舔,網(wǎng)上很多博客文章都是轉(zhuǎn)載的,很少有能直接運行的链瓦,研究了一整天才弄出來)

這里用到了第三方庫:Aspects,用cocoaPods進行集成 pod 'Aspects'

1拆魏、創(chuàng)建一個繼承與NSObject的EJAspectManager類

EJAspectManager.h

@interface EJAspectManager : NSObject
+(void)trackAspectHooks;
@end

EJAspectManager.m

#import "EJAspectManager.h"
#import "Aspects/Aspects.h"
@implementation EJAspectManager

+(void)trackAspectHooks{

    [EJAspectManager trackViewAppear];
    [EJAspectManager trackBttonEvent];
}


#pragma mark -- 監(jiān)控統(tǒng)計用戶進入此界面的時長,頻率等信息
+ (void)trackViewAppear{
    
    [UIViewController aspect_hookSelector:@selector(viewWillAppear:)
                              withOptions:AspectPositionBefore
                               usingBlock:^(id<AspectInfo> info){
                                   
                                   //用戶統(tǒng)計代碼寫在此處
                                   DDLogDebug(@"[打點統(tǒng)計]:%@ viewWillAppear",NSStringFromClass([info.instance class]));
                                   NSString *className = NSStringFromClass([info.instance class]);
                                   DLog(@"className-->%@",className);
                                   [MobClick beginLogPageView:className];//(className為頁面名稱
                                   
                               }
                                    error:NULL];
    
    
    [UIViewController aspect_hookSelector:@selector(viewWillDisappear:)
                              withOptions:AspectPositionBefore
                               usingBlock:^(id<AspectInfo> info){
                                   
                                   //用戶統(tǒng)計代碼寫在此處
                                   DDLogDebug(@"[打點統(tǒng)計]:%@ viewWillDisappear",NSStringFromClass([info.instance class]));
                                   NSString *className = NSStringFromClass([info.instance class]);
                                   DLog(@"className-->%@",className);
                                   [MobClick endLogPageView:className];
                                   
                               }
                                    error:NULL];
    
    //other hooks ... goes here
    //...
}

#pragma mark --- 監(jiān)控button的點擊事件
+ (void)trackBttonEvent{
    
    __weak typeof(self) ws = self;

    //設(shè)置事件統(tǒng)計
    //放到異步線程去執(zhí)行
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //讀取配置文件慈俯,獲取需要統(tǒng)計的事件列表
        NSString *path = [[NSBundle mainBundle] pathForResource:@"EventList" ofType:@"plist"];
        NSDictionary *eventStatisticsDict = [[NSDictionary alloc] initWithContentsOfFile:path];
        for (NSString *classNameString in eventStatisticsDict.allKeys) {
            //使用運行時創(chuàng)建類對象
            const char * className = [classNameString UTF8String];
            //從一個字串返回一個類
            Class newClass = objc_getClass(className);
            
            NSArray *pageEventList = [eventStatisticsDict objectForKey:classNameString];
            for (NSDictionary *eventDict in pageEventList) {
                //事件方法名稱
                NSString *eventMethodName = eventDict[@"MethodName"];
                SEL seletor = NSSelectorFromString(eventMethodName);
                NSString *eventId = eventDict[@"EventId"];
                
                [ws trackEventWithClass:object_getClass(newClass) selector:seletor eventID:eventId];
                [ws trackTableViewEventWithClass:object_getClass(newClass) selector:seletor eventID:eventId];
                [ws trackParameterEventWithClass:object_getClass(newClass) selector:seletor eventID:eventId];
            }
        }
    });
}

#pragma mark -- 監(jiān)控button和tap點擊事件(不帶參數(shù))
+ (void)trackEventWithClass:(Class)klass selector:(SEL)selector eventID:(NSString*)eventID{
    
    [klass aspect_hookSelector:selector withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo) {
        
        NSString *className = NSStringFromClass([aspectInfo.instance class]);
        NSLog(@"className--->%@",className);
        NSLog(@"event----->%@",eventID);
        if ([eventID isEqualToString:@"xxx"]) {
            [EJServiceUserInfo isLogin]?[MobClick event:eventID]:[MobClick event:@"???"];
        }else{
            [MobClick event:eventID];
        }
    } error:NULL];
}


#pragma mark -- 監(jiān)控button和tap點擊事件(帶參數(shù))
+ (void)trackParameterEventWithClass:(Class)klass selector:(SEL)selector eventID:(NSString*)eventID{
    
    [klass aspect_hookSelector:selector withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo,UIButton *button) {
        
        NSLog(@"button---->%@",button);
        NSString *className = NSStringFromClass([aspectInfo.instance class]);
        NSLog(@"className--->%@",className);
        NSLog(@"event----->%@",eventID);
        
    } error:NULL];
}


#pragma mark -- 監(jiān)控tableView的點擊事件
+ (void)trackTableViewEventWithClass:(Class)klass selector:(SEL)selector eventID:(NSString*)eventID{
    
    [klass aspect_hookSelector:selector withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> aspectInfo,NSSet *touches, UIEvent *event) {
        
        NSString *className = NSStringFromClass([aspectInfo.instance class]);
        NSLog(@"className--->%@",className);
        NSLog(@"event----->%@",eventID);
        NSLog(@"section---->%@",[event valueForKeyPath:@"section"]);
        NSLog(@"row---->%@",[event valueForKeyPath:@"row"]);
        NSInteger section = [[event valueForKeyPath:@"section"]integerValue];
        NSInteger row = [[event valueForKeyPath:@"row"]integerValue];
        
        //統(tǒng)計事件
        if (section == 0 && row == 1) {
            [MobClick event:eventID];
        }
        
    } error:NULL];
}
@end

2渤刃、這樣我們在appDelegate里面直接調(diào)用下面的代碼就可以達到全局統(tǒng)計的目的了!

[EJAspectManager trackAspectHooks];

3贴膘、上面涉及到的EventPlist是新創(chuàng)建的plist文件卖子,設(shè)置方式如下:

B091368F-0F17-4671-9A5E-F6527BA5CD31.png

需要demo的同學(xué)麻煩打個賞(金額隨便給),留個郵箱刑峡,我會發(fā)到你們郵箱里洋闽,謝謝支持!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末突梦,一起剝皮案震驚了整個濱河市诫舅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阳似,老刑警劉巖骚勘,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡俏讹,警方通過查閱死者的電腦和手機当宴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泽疆,“玉大人户矢,你說我怎么就攤上這事⊙程郏” “怎么了梯浪?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瓢娜。 經(jīng)常有香客問我挂洛,道長,這世上最難降的妖魔是什么眠砾? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任虏劲,我火速辦了婚禮,結(jié)果婚禮上褒颈,老公的妹妹穿的比我還像新娘柒巫。我一直安慰自己,他們只是感情好谷丸,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布堡掏。 她就那樣靜靜地躺著,像睡著了一般刨疼。 火紅的嫁衣襯著肌膚如雪泉唁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天币狠,我揣著相機與錄音游两,去河邊找鬼砾层。 笑死漩绵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肛炮。 我是一名探鬼主播止吐,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼侨糟!你這毒婦竟也來了碍扔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秕重,失蹤者是張志新(化名)和其女友劉穎不同,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡二拐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年服鹅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片百新。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡企软,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饭望,到底是詐尸還是另有隱情仗哨,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布铅辞,位于F島的核電站厌漂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏斟珊。R本人自食惡果不足惜桩卵,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望倍宾。 院中可真熱鬧雏节,春花似錦、人聲如沸高职。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怔锌。三九已至寥粹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間埃元,已是汗流浹背涝涤。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留岛杀,地道東北人阔拳。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像类嗤,于是被迫代替她去往敵國和親糊肠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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

  • 概述在iOS開發(fā)中UITableView可以說是使用最廣泛的控件遗锣,我們平時使用的軟件中到處都可以看到它的影子货裹,類似...
    liudhkk閱讀 9,049評論 3 38
  • WebSocket-Swift Starscream的使用 WebSocket 是 HTML5 一種新的協(xié)議。它實...
    香橙柚子閱讀 23,869評論 8 183
  • 跨過時間海 我和矗立在鼓浪嶼的鄭成功雕像相遇 巨浪拍打的胸腔 瞬間激起了三千怒發(fā) 無邊的鄉(xiāng)愁 不知從何時起 已鋪滿...
    杏壇皓月閱讀 142評論 1 2
  • 今天在和一個同學(xué)聊天赋兵,她說她在和另一個同學(xué)在同學(xué)群聊天時,那個同學(xué)看到一個網(wǎng)上的故事而傷心難過了搔预,她安慰了她毡惜。她又...
    楓葉飄逸閱讀 489評論 0 0
  • 基本語法 ES6 允許使用“箭頭”(=>)定義函數(shù)。 上面的箭頭函數(shù)相當(dāng)于: 如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù)...
    iyimao閱讀 689評論 0 6