无侵入的埋点方案如何实现
运行时方法替换方式进行埋点
#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@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+ (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]; }}事件唯一标识
小结
Last updated
Was this helpful?
