# 无侵入埋点

## 方法1 ，通过hook方法交换系统方法，在交换的方法中实现埋点

```
#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
```

这个方法利用运行时 method\_exchangeImplementations接口将方法的实现进行了交换，原方法调用时就会被hook住，从而去执行指定的方法

## hook1：统计页面

hook方法的应用-统计每个页面进入的次数和页面的停留时间 1.方法，通过对vc的生命周期进行埋点，所以要创建一个vc的分类，如下代码

```
@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
```

通过NSStringFromClass(\[self class])]来区分不同的vc

## hook2：统计UIButton点击

hook方法的应用-统计点击&#x20;

1.找到点击事件：sendAction：to：forEvent：&#x20;

2.能处理相同UIButton子类在不同视图类的埋点&#x20;

3.通过action选择器名NSStringFromSelector(action) + 视图类名NSStringFromClass(\[self class])]组合成唯一的标识&#x20;

{% embed url="<https://4.这里的action是方法名>" %}

```
+ (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];
    }
}
```

## hook3：统计一个视图下相同UIButton的不同实例

1.通过视图层级+视图的索引来确定唯一标识

## hook4：统计cell的点击

1.通过indexPath来唯一确定唯一标识

## hook5：对UIAlertController埋点

1.通过alert的内容来确定唯一的标识
