这两天在搞一个统计模块,把碰到的问题和一些讨论记录下来,所以本文没有答案,没有解决方案,仅是讨论而已。
我要做什么?
我现在做的是一个 app 里面的用户行为统计,简单来说就是记录下用户从哪个页面跳转到哪个页面,在页面上都点击了哪些按钮,点击了几次等等之类的东西。
统计工具用的是现成的 Google Analytics,Flurry,MixPanel,我要做的就是将他们集成进我的统计模块代码,并进行各种业务事件的统计。
以Flurry为例,当点击登录按钮clickShootButtonAction被调用要做一条用户行为统计,统计代码是这样的
- -?(IBAction)clickShootButtonAction
- {
- ????//?执行下面这句话,在?Flurry?的后台就能看到这个事件的记录
- ????[Flurry?logEvent:@"点击拍照按钮"];
- }
或者
- -?(IBAction)clickShootButtonAction
- {
- ????//?执行下面这句话,在?Flurry?的后台就能看到这个事件的记录
- ????//?与上面不同的是,这边记录的是用户的一条行为路径
- ????//?表示:用户拍照后,在照片分享页,将照片分享到了?Facebook
- ????[Flurry?logEvent:@"保存分享照片"?withParameters:{@"分享到":@"Facebook"}];
- }
而在应用里面记录上百个用户行为是很正常的事情。也就说类似上面的这种代码在 Controller 里面可能要出现几百次,还散落在各处。
这是正常的吗?
如果这些代码遍布我们的工程,使得统计模块和业务代码耦合度极高,造成剥离困难,无法重用等等各种的问题,写起来手累心也累。
对我们来说,最理想的情况下,Controller 里面这种代码越少越好,***是一行都没有,包含个头文件就能自动统计那该多好。因为如果要剥离统计代码,或者更换统计方式,都是非常方便。
但实际情况不容乐观!!!
有解吗?
根据统计的事件,我们把需要统计的方法大致归类为以下三种,统计剥离的难度也逐级递增
- 类似 viewDidLoad、viewWillAppear 这种 ViewController 的生命周期的方法;
- 调用就进行一次事件统计的方法;
- 在方法内,满足条件才统计的方法;
那么问题来了,如果我们不希望在 Controller 里面直接添加统计代码,应该怎么统计上面的三种方法?
剥离统计 ViewController 生命周期的统计代码
这类方法的统计比较简单,写个 UIViewController 的 category,hook UIViewController 的中需要统计的方法,然后将头文件塞到要统计的 ViewController 即可。
剥离调用一次就统计的统计代码
这个统计代码的剥离比较麻烦,麻烦的点在于这些方法是根据业务逻辑产生的,每个 ViewController 中的方法都不一致,没法用统一的方式来处理。
关于这类代码的剥离,我查了一些资料,请教一些同学,又在一些技术群讨论了下,确实可以剥离,但方法都不太可靠,所以不建议使用,以下一一列举。
- 方法一:AOP + SPOC 配置文件?
这个方法来自最近上传的《禅与 Objective-C 编程艺术》***一小节 面向切面编程 中。通过在 SPOC 配置文件中添加类名和方法,hook 类里面的方法,然后进行统计。
乍看之下,这个思路非常不错,写起来爽歪歪,要添加统计代码时,只要在配置文件中加一下就 OK,嗨到不行。但后来一想这种方式实际执行起来是会有问题的。
首先统计模块代码和业务代码是分散在两个地方,统计是根据具体的类名和方法名的字符串来 hook 后进行统计操作。因为统计模块比较独立,由一个单独的人来写,其他人去写业务代码。业务开发的同学随意修改个方法名还是比较正常的。当业务开发的同学改了个方法名,一般不会想到统计这边也要改;统计这边的同学也不知道业务的同学改了什么。所以这种只有调试时,碰到统计异常才会去检查这里,可维护性太差。
所以这种方法是写的人爽,维护的人非常非常的不爽!?
所以这种不推荐使用!
- 方法二:约定方法名?
从开发的开始就约定好,比如需要统计的函数(例如 IBAction 之类的,按照约定命名),然后 hook objc-sendmessage 函数,统计所需的方法。这个方法其实和方法一类似,所以也有同样的问题。而且人为约定没有特别强的约束力,稍不留神就忘了方法名的约定,而且也没有编译器警告、错误的提示,根本就不会想到这时候要按照约定的规则命名。再且需求是经常变动的,有些原先不需要统计的内容,后来突然又要统计了,按照这种规则就要改方法名,整体的感觉就是场面太混乱了。
所以这种也不推荐使用!
剥离满足条件才统计的统计代码
无解!
想过去真真是无解啊!因为我们前面所用的方法和思路基本离不开 AOP,而 AOP 本身是 hook 一整个方法,在方法前后添加一些自定义的操作。AOP 是没有办法了解方法内部是什么样的,更何谈去统计方法内满足了一定条件再统计事件。
怎么办?
老老实实的将统计代码写到相关的方法里面吧,真没辙了!
***的忧伤
折腾了这么些来回,结果还是没法将统计代码和业务代码分开。原以为统计模块应该是一个独立的模块,结果却捅到各个Controller代码里面去,实在令人忧伤。
那么到底是什么原因造成了这样一个结局?
回头看看,我们一开始就默认了统计模块是一个独立的模块,应该与业务逻辑分开来。但实际上统计是和业务紧密结合的一个模块,所以是不是可以这么想。统计模块的代码也属于业务逻辑呢?
不管怎样,这么想就可以心安理得的把统计代码写进 Controller 了~