入门客AI创业平台(我带你入门,你带我飞行)
博文笔记

自定义可复用统计SDK设计

创建时间:2017-03-13 投稿人: 浏览次数:873

之前换工作,有家公司福利待遇各方面都特别满意,十分想去,但是面试碰到个问题,让我尴尬了,虽然本人到目前为止从事iOS开发三年多,但大多独立开发,这问题还真没深入去考虑过。下面我具体讲讲遇到的问题。

首先,那个公司的项目要自己开发统计的sdk,不用第三方库,主要是考虑到数据安全的问题,这其实没什么,自己开发就自己开发吧,然后,面试官问我开发这个sdk的思路是什么,应该怎么去做,然后我滔滔不绝的把友盟统计的实现原理与步骤吧啦吧啦讲讲一遍,但是,他紧接着就问我一个问题,说:你不觉得这样做,耦合度很高吗?统计的代码分散在工程的各个角落,维护或修改起来多麻烦?当时我就震惊了……what?……你如果要统计按钮点击次数,你不在按钮实现方法里加个统计的代码,那要怎么做呢?因为从来没考虑过这方面的问题,所以当时我的脑子是空白的。

理所当然的面试失败,回到家,我痛定思痛,深入研究了一下,在这里写一下我的心得,有不对的地方希望留言指正。

首先传统统计,例如友盟,他有以下几个缺点:

1.复用性差,这部分埋点代码很难给其他项目复用,复用性基本为零

2.工作量大,尤其在页面较多时,需要修改的代码也多

3.引入“脏代码”,不易维护,尤其是稍微大一点的app,这样的代码几乎遍地都是 (这里的脏代码指的是用户行为分析这种业务其实跟主业务没有关系,不应该保持如此高的耦合度,因为这些代码会干扰我们对项目主业务的维护)

因此,采用这种传统的代码,虽然直观,方便,但是在可复用性以及可维护性来说,有很大的缺陷。我在网上看了很多的博客论坛之类的,总结一下可以用runtime来有效的解决这一问题。

由于Runtime知识不属于本文的重点,这里只简单介绍。

在iOS中,我们可以在运行时替换两个方法的实现,达到“勾住”某个方法并注入代码的目的。具体做法是:

重载类的“+(void)load”方法,在程序加载到内存时利用Runtime的method_exchangeImplementations等接口将方法(设为M)的实现互相交换。当方法M被调用时就会被勾住(Hook),执行我们的方法。

这种技术也称为Method Swizzling,属于面向切面编程(Aspect-Oriented Programming)的一种实现。

替换两个方法的实现,代码一般长酱紫:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @interface WHookUtility : NSObject + (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector; @end @implementation WHookUtility + (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector {     Class class = cls;     Method originalMethod = class_getInstanceMethod(class, originalSelector);     Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);     BOOL didAddMethod =     class_addMethod(class,     originalSelector,     method_getImplementation(swizzledMethod),     method_getTypeEncoding(swizzledMethod));     if (didAddMethod) {         class_replaceMethod(class,         swizzledSelector,         method_getImplementation(originalMethod),         method_getTypeEncoding(originalMethod));     else {         method_exchangeImplementations(originalMethod, swizzledMethod);     } } @end

这个WHookUtility工具类下文会用到。比如现在我们要勾住UIViewController的viewWillAppear:方法,可以这样做:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @implementation UIViewController (userStastistics) + (void)load {     static dispatch_once_t onceToken;     dispatch_once(&onceToken, ^{     SEL originalSelector = @selector(viewWillAppear:);     SEL swizzledSelector = @selector(swiz_viewWillAppear:);     [WHookUtility swizzlingInClass:[self class] originalSelector:originalSelector swizzledSelector:swizzledSelector]; }); } #pragma mark - Method Swizzling - (void)swiz_viewWillAppear:(BOOL)animated {     //插入需要执行的代码     NSLog(@"我在viewWillAppear执行前偷偷插入了一段代码");     //不能干扰原来的代码流程,插入代码结束后要让本来该执行的代码继续执行     [self swiz_viewWillAppear:animated]; } @end

基于运行时的埋点方案

为了便于下文叙述,先引入一个简单的项目,共有两个页面(HomeViewController,DetailViewController),如下:

1461220770393707.gif

需求是:

  • 统计两个页面的展示与离开次数

  • 统计收藏、分享单击事件的次数

  • 对现有工程代码影响越小越好


1)统计两个页面的展示与离开次数

这部分应该比较直观了,摒弃掉在每个controller中埋点的方式,我们对UIViewController添加category从而Hook到viewWillAppear:与viewWillDisappear:。在这两个方法中注入埋点代码:

1053533-2358da1c54655daf.jpg

这时候问题来了,项目中每个页面都会有自己的页面事件编号(pageEventID),此处的埋点代码如何知道要发送什么pageEventID给服务端呢?轻松祭出if-else神器:


1 2 3 4 5 6 7 8 9 10 11
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。
  • 上一篇:没有了
  • 下一篇:没有了
未上传头像