af 的方法交换处理
static inline void af_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
if (class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
数据采集
- uiviewcontroller的生命周期,用于测算页面停留时间加载时间等
1.viewDidAppear上报页面进入事件。
2.viewDidDisappear上报页面退出事件
即可得出用户访问页面路径,两个事件时间戳之差即为用户在页面停留的时间。
统计数据字段
1.PAGE_ID,当前页面的标识。 【一般用类名】
2.SOURCE_ID,当前页面的前一个页面的标识。【获取上一个页面的类名】
3.TYPE_ID,当前页面一些关键信息。
4.TIMESTAMP,当前事件生成的时间戳。
5.页面进入和退出的事件,均上报上述的数据结构。
6.跟进上面的记录通过计算可以获得VC的路径
+ (void)install {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[Tool replaceImplementationOfKnownSelector:@selector(viewDidAppear:) onClass:[self class]];
[Tool replaceImplementationOfKnownSelector:@selector(viewDidDisappear:) onClass:[self class]];
});
}
- (void)br_viewDidAppear:(BOOL)animated {
NSLog(@"[%@--%@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self br_viewDidAppear:animated];
}
- (void)br_viewDidDisappear:(BOOL)animated {
NSLog(@"[%@--%@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
[self br_viewDidDisappear:animated];
}
- UIControl(UISwitch,UIStepper,UISegmentedControl, UINavigationButton,UISlider,UIButton)类控件的点击事件
+ (void)install {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[Tool replaceImplementationOfKnownSelector:@selector(sendAction:to:from:forEvent:) onClass:[self class]];
});
}
- (BOOL)br_sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event {
NSLog(@"=====");
return [self br_sendAction:action to:target from:sender forEvent:event];
}
- UIView上的GestureRecognizer触摸事件
在uiview的分类中添加target
#import <objc/runtime.h>
+ (void)startTracker {
Method addGestureRecognizerMethod = class_getInstanceMethod(self, @selector(addGestureRecognizer:));
Method ddAddGestureRecognizerMethod = class_getInstanceMethod(self, @selector(dd_addGestureRecognizer:));
method_exchangeImplementations(addGestureRecognizerMethod, ddAddGestureRecognizerMethod);
}
- (void)test:(UIGestureRecognizer *)gest {
}
- (void)dd_addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
[gestureRecognizer addTarget:self action:@selector(test:)];
[self dd_addGestureRecognizer:gestureRecognizer];
- UICollectionView和UITableView的cell点击事件
@implementation UITableView (DDAutoTracker)
+ (void)startTracker {
Method setDelegateMethod = class_getInstanceMethod(self, @selector(setDelegate:));
Method ddSetDelegateMethod = class_getInstanceMethod(self, @selector(dd_setDelegate:));
method_exchangeImplementations(setDelegateMethod, ddSetDelegateMethod);
}
- (void)dd_setDelegate:(id <UITableViewDelegate>)delegate {
//只监听UITableView
if (![self isMemberOfClass:[UITableView class]]) {
return;
}
[self dd_setDelegate:delegate];
if (delegate) {
Class class = [delegate class];
SEL originSelector = @selector(tableView:didSelectRowAtIndexPath:);
SEL swizzlSelector = NSSelectorFromString(@"dd_didSelectRowAtIndexPath");
BOOL didAddMethod = class_addMethod(class, swizzlSelector, (IMP)dd_didSelectRowAtIndexPath, "v@:@@");
if (didAddMethod) {
Method originMethod = class_getInstanceMethod(class, swizzlSelector);
Method swizzlMethod = class_getInstanceMethod(class, originSelector);
method_exchangeImplementations(originMethod, swizzlMethod);
}
}
}
void dd_didSelectRowAtIndexPath(id self, SEL _cmd, id tableView, NSIndexPath *indexpath) {
SEL selector = NSSelectorFromString(@"dd_didSelectRowAtIndexPath");
((void(*)(id, SEL,id, NSIndexPath *))objc_msgSend)(self, selector, tableView, indexpath);
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexpath];
NSString *targetString = NSStringFromClass([self class]);
NSString *actionString = NSStringFromSelector(_cmd);
NSString *eventId = [NSString stringWithFormat:@"%@&&%@",targetString,actionString];
NSDictionary *infoDictionary = [cell ddInfoDictionary];
[[DDAutoTrackerOperation sharedInstance] sendTrackerData:eventId
info:infoDictionary];
}
@end
@implementation UICollectionView (DDAutoTracker)
+ (void)startTracker {
Method setDelegateMethod = class_getInstanceMethod(self, @selector(setDelegate:));
Method ddSetDelegateMethod = class_getInstanceMethod(self, @selector(dd_setDelegate:));
method_exchangeImplementations(setDelegateMethod, ddSetDelegateMethod);
}
- (void)dd_setDelegate:(id <UICollectionViewDelegate>)delegate {
//只监听UICollectionView
if (![self isMemberOfClass:[UICollectionView class]]) {
return;
}
[self dd_setDelegate:delegate];
if (delegate) {
Class class = [delegate class];
SEL originSelector = @selector(collectionView:didSelectItemAtIndexPath:);
SEL swizzlSelector = NSSelectorFromString(@"dd_didSelectItemAtIndexPath");
BOOL didAddMethod = class_addMethod(class, swizzlSelector, (IMP)dd_didSelectItemAtIndexPath, "v@:@@");
if (didAddMethod) {
Method originMethod = class_getInstanceMethod(class, swizzlSelector);
Method swizzlMethod = class_getInstanceMethod(class, originSelector);
method_exchangeImplementations(originMethod, swizzlMethod);
}
}
}
void dd_didSelectItemAtIndexPath(id self, SEL _cmd, id collectionView, NSIndexPath *indexpath) {
SEL selector = NSSelectorFromString(@"dd_didSelectItemAtIndexPath");
((void(*)(id, SEL,id, NSIndexPath *))objc_msgSend)(self, selector, collectionView, indexpath);
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexpath];
NSString *targetString = NSStringFromClass([self class]);
NSString *actionString = NSStringFromSelector(_cmd);
NSString *eventId = [NSString stringWithFormat:@"%@&&%@",targetString,actionString];
NSDictionary *infoDictionary = [cell ddInfoDictionary];
[[DDAutoTrackerOperation sharedInstance] sendTrackerData:eventId
info:infoDictionary];
}
@end
- UITabBar、UIAlertView、UIActionSheet等的点击事件
hook delegate 然后hook点击事件的代理方法
唯一标识 使用title message 按钮内容 再加上路径
ios 9
if ([NSStringFromClass([view class]) isEqualToString:@"_UIAlertControllerView"]) {
Ivar ivar = class_getInstanceVariable([view class], "_actionViews");
NSMutableArray *actionviews = object_getIvar(view, ivar);
for (UIView *actionview in actionviews) {
CGPoint point = [gesture locationInView:actionview];
if ([NSStringFromClass([actionview class]) isEqualToString:@"_UIAlertControllerActionView"] &&
point.x > 0 && point.x < CGRectGetWidth(actionview.bounds) &&
point.y > 0 && point.y < CGRectGetHeight(actionview.bounds) &&
gesture.state == UIGestureRecognizerStateBegan) {
......... //进行埋点操作
}
}
}
ios 10
if ([NSStringFromClass([view class]) isEqualToString:@"_UIAlertControllerInterfaceActionGroupView"]) {
NSMutableArray *targets = [gesture valueForKey:@"_targets"];
id targetContainer = targets[0];
id targetOfGesture = [targetContainer valueForKey:@"_target"];
if ([targetOfGesture isKindOfClass:[NSClassFromString(@"UIInterfaceActionSelectionTrackingController") class]]) {
Ivar ivar = class_getInstanceVariable([targetOfGesture class], "_representationViews");
NSMutableArray *representationViews = object_getIvar(targetOfGesture, ivar);
for (UIView *representationView in representationViews) {
CGPoint point = [gesture locationInView:representationView];
if ([NSStringFromClass([representationView class]) isEqualToString:@"_UIInterfaceActionCustomViewRepresentationView"] &&
point.x > 0 && point.x < CGRectGetWidth(representationView.bounds) &&
point.y > 0 && point.y < CGRectGetHeight(representationView.bounds) &&
gesture.state == UIGestureRecognizerStateBegan) {
......... //进行埋点操作
}
}
}
}
#import <objc/runtime.h>
#import <objc/message.h>
#import "DDAutoTrackerOperation.h"
#import "NSObject+DDAutoTracker.h"
@implementation UIAlertView (test)
+ (void)load {
[self startTracker];
}
+ (void)startTracker {
Method setDelegateMethod = class_getInstanceMethod(self, @selector(setDelegate:));
Method ddSetDelegateMethod = class_getInstanceMethod(self, @selector(dd_setDelegate:));
method_exchangeImplementations(setDelegateMethod, ddSetDelegateMethod);
}
- (void)dd_setDelegate:(id <UIAlertViewDelegate>)delegate {
//只监听UICollectionView
if (![self isMemberOfClass:[UIAlertView class]]) {
return;
}
[self dd_setDelegate:delegate];
if (delegate) {
Class class = [delegate class];
SEL originSelector = @selector(alertView:clickedButtonAtIndex:);
SEL swizzlSelector = NSSelectorFromString(@"br_clickedButtonAtIndex");
BOOL didAddMethod = class_addMethod(class, swizzlSelector, (IMP)br_clickedButtonAtIndex, "v@:@@");
if (didAddMethod) {
Method originMethod = class_getInstanceMethod(class, swizzlSelector);
Method swizzlMethod = class_getInstanceMethod(class, originSelector);
method_exchangeImplementations(originMethod, swizzlMethod);
}
}
}
void br_clickedButtonAtIndex(id self, SEL _cmd, id alertView, NSInteger buttonIndex) {
SEL selector = NSSelectorFromString(@"br_clickedButtonAtIndex");
((void(*)(id, SEL,id, NSInteger))objc_msgSend)(self, selector, alertView, buttonIndex);
NSLog(@"hook 点击的第几个 %ld", buttonIndex);
}
@end
-
RN采集
-
app内OpenURL 主要是应用直接的跳转回调等
hook UIApplicationDelegate协议对象,直接hook appdelegate或者写它的分类,可能会失败(原因:某些用户可能会自定义文件名,默认加前缀,XXAppdelegate)
还有其生命周期方法一并hook
需要判断方法是否实现,未实现添加实现(本人一直操作失败),已实现进行交换
application:handleOpenURL:
application:openURL:options:
- UItextview uitextfiled uisearchbar
根据代理处理
数据缓存
- 默认的存储模式【个人想法内存】
- 考虑用户杀掉程序或者崩溃时数据如何持久化存储
- 持久化保存方式【json?plist?db?】哪种性能更好?
- 文件量限制,时间限制 【记录10条数据就开始上传一次,或者1分钟一次】
- 缓存格式?
网友评论