无侵入埋点方案如何实现

埋点可以解决两大类问题: 一是了解用户使用App的行为, 二是降低分析线上问题的难度

常见的埋点方式: 代码埋点、可视化埋点、无埋点

代码埋点: 通过手写代码, 精确的在需要埋点的代码处加上埋点的代码, 存在开发工作量大, 后期难以维护的问题
可视化埋点: 增加修改的工作可视化了, 提升体验
无埋点: 埋点代码不会出现在业务代码中, 容易管理和维护, 缺点在于, 埋点成本高, 后期解析复杂, 优点是节省大量的开发和维护成本

运行时方法替换方式进行埋点

常见三种埋点: 页面进入次数、页面停留时间、点击事件
利用运行时method_exchangeImplementations接口将方法的实现进行了交换, 原方法调用时就会被hook住, 从而去执行指定的方法

 #import "SMHook.h"
 #import 

 @implementation SMHook
 + (void)hookClass:(Class)classObject fromSelector:(SEL)fromSelector toSelector:(SEL)toSe
 Class class = classObject;
 // 得到被替换类的实例方法
 Method fromMethod = class_getInstanceMethod(class, fromSelector);
 // 得到替换类的实例方法
 Method toMethod = class_getInstanceMethod(class, toSelector);
 // class_addMethod 返回成功表示被替换的方法没实现,然后会通过 class_addMethod 方法先实现
 if(class_addMethod(class, fromSelector, method_getImplementation(toMethod), method_g
 // 进行方法的替换
 class_replaceMethod(class, toSelector, method_getImplementation(fromMethod), met
 } else {
 // 交换 IMP 指针
 method_exchangeImplementations(fromMethod, toMethod);
 }
 }
 @end

页面进入次数、页面停留时间都需要对UIViewController生命周期进行埋点, 可以创建一个UIViewController的Catagory

@implementation UIViewController (logger)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 通过 @selector 获得被替换和替换方法的 SEL,作为 SMHook:hookClass:fromeSelector:toSelector 的参数传入 
        SEL fromSelectorAppear = @selector(viewWillAppear:);
        SEL toSelectorAppear = @selector(hook_viewWillAppear:);
        [SMHook hookClass:self fromSelector:fromSelectorAppear toSelector:toSelectorAppear];
        
        SEL fromSelectorDisappear = @selector(viewWillDisappear:);
        SEL toSelectorDisappear = @selector(hook_viewWillDisappear:);
        
        [SMHook hookClass:self fromSelector:fromSelectorDisappear toSelector:toSelectorDisappear];
    });
}

- (void)hook_viewWillAppear:(BOOL)animated {
    // 先执行插入代码,再执行原 viewWillAppear 方法
    [self insertToViewWillAppear];
    [self hook_viewWillAppear:animated];
}
- (void)hook_viewWillDisappear:(BOOL)animated {
    // 执行插入代码,再执行原 viewWillDisappear 方法
    [self insertToViewWillDisappear];
    [self hook_viewWillDisappear:animated];
}

- (void)insertToViewWillAppear {
    // 在 ViewWillAppear 时进行日志的埋点
    [[[[SMLogger create]
       message:[NSString stringWithFormat:@"%@ Appear",NSStringFromClass([self class])]]
      classify:ProjectClassifyOperation]
     save];
}
- (void)insertToViewWillDisappear {
    // 在 ViewWillDisappear 时进行日志的埋点
    [[[[SMLogger create]
       message:[NSString stringWithFormat:@"%@ Disappear",NSStringFromClass([self class])]]
      classify:ProjectClassifyOperation]
     save];
}
@end

对于点击事件, 可以通过运行时方法替换的方式进行无侵入埋点, 找到点击事件的方法sendAction:to:forEvent:, 然后在+load()方法使用SMHook替换
UIButton在一个视图类中可能有多个不同的继承类, 相同UIButton的子类在不同视图类的埋点也要区别开, 需要通过“action选择器名 NSStringFromSelector(action)”+“视图类名NSStringFromClass([target class])”组合成一个唯一的标识, 进行埋点

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 通过 @selector 获得被替换和替换方法的 SEL,作为 SMHook:hookClass:fromeSelector:toSelector 的参数传入
        SEL fromSelector = @selector(sendAction:to:forEvent:);
        SEL toSelector = @selector(hook_sendAction:to:forEvent:);
        [SMHook hookClass:self fromSelector:fromSelector toSelector:toSelector];
    });
}

- (void)hook_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
    [self insertToSendAction:action to:target forEvent:event];
    [self hook_sendAction:action to:target forEvent:event];
}
- (void)insertToSendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
    // 日志记录
    if ([[[event allTouches] anyObject] phase] == UITouchPhaseEnded) {
        NSString *actionString = NSStringFromSelector(action);
        NSString *targetName = NSStringFromClass([target class]);
        [[[SMLogger create] message:[NSString stringWithFormat:@"%@ %@",targetName,actionString]] save];
    }
}

你可能感兴趣的:(无侵入埋点方案如何实现)