Wrappres' Studio.

埋点

字数统计: 595阅读时长: 2 min
2019/10/26 Share

iOS 埋点主要分为三种:

  • 代码埋点:通过手写代码进行埋点,能够很精确的在需要埋点的代码处加上埋点的代码,具有很强的灵活性。但存在开发工作量大,埋点代码到处都是,后期难以维护但问题。
  • 可视化埋点:将埋点的增加修改的工作可视化了,提高了增加和维护埋点的体验。
  • 无埋点:全埋点,埋点代码不会出现在业务代码中,很容易维护和管理。缺点在于,埋点成本高,后期的解析也比较复杂,view_path的不确定性。这种方案不能解决所有的埋点需求,但对于大量通用的埋点需求来说,能够节省大量的开发和维护成本。

利用运行时替换方法进行埋点

先写一个替换方法的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import "SMHook.h"
#import <objc/runtime.h>

@implementation SMHook

+ (void)hookClass:(Class)classObject fromSelector:(SEL)fromSelector toSelector:(SEL)toSelector {
Class class = classObject;
// 得到被替换类的实例方法
Method fromMethod = class_getInstanceMethod(class, fromSelector);
// 得到替换类的实例方法
Method toMethod = class_getInstanceMethod(class, toSelector);

// class_addMethod 返回成功表示被替换的方法没实现,然后会通过 class_addMethod 方法先实现;返回失败则表示被替换方法已存在,可以直接进行 IMP 指针交换
if(class_addMethod(class, fromSelector, method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
// 进行方法的替换
class_replaceMethod(class, toSelector, method_getImplementation(fromMethod), method_getTypeEncoding(fromMethod));
} else {
// 交换 IMP 指针
method_exchangeImplementations(fromMethod, toMethod);
}
}
@end

记录页面进入次数,页面停留时间需要对 UIViewController 的生命周期进行埋点。一般用NSStringFromClass([self class]]获取类名来区分不同 ViewController。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@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

CATALOG
  1. 1. 利用运行时替换方法进行埋点