方法交换
+(void)load
-
load当类加载进内存的时候调用,而且不管有没有子类,都只会调用一次。在main函数之前调用。
-
用途:
-
1.可以在新建类中实现一些配置信息。
-
2.runtime交换方法的时候,因为只需要交换一次方法,所有可以在该方法中交换方法的代码,用于只实现一次的代码。
-
+(void)initialize
-
当类被初始化的时候调用,可能会被调用多次,若是没有子类,则只会调用一次。若是有子类的话该方法会调用多次,若是子类的继承关系,先会去调用父类的+(void)initilize方法,然后再去调用子类的+(void)initilize方法,(若是继承关系,调用某个方法的时候,先会去父类中查找,若是父类中没有方法的实现就去子类中查找)
-
用途:
-
1:在设置导航栏的全局背景的时候,只需要设置一次,可以重写该方法设置,最好是在该方法判断子类,若是自己,则实现设置全局导航栏的方法,若不是自己则跳过实现
-
2:在创建数据库代码的时候,可以在该方法中去创建,保证只初始化一次数据库实例,也可以用dispatch或是懒加载的方法中初始化数据库实例,也能保证只初始化一次数据库实例。其中也可以在+ (void)initialize方法中用dispatch也能保证即使有子类也只会初始化一次
+ (void)load{[super load]; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 假如要打开controller的统计 ,则把下面这行代码打开 __gbh_tracer_swizzleMethod([self class], @selector(viewDidAppear:), @selector(__gbh_tracer_viewDidAppear:)); });
}
void __gbh_tracer_swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector){
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);
}
}
看回第一块代码,红色的viewDidAppear是即将被我hook的方法,__gbh_tracer_viewDidAppear 则是我需要实现的方法
- (void)__gbh_tracer_viewDidAppear:(BOOL)animated{
[self __gbh_tracer_viewDidAppear:animated]; //由于方法已经被交换,这里调用的实际上是viewDidAppear:方法
//设置不允许发送数据的Controller
NSArray *filter = @[@"UINavigationController",@"UITabBarController"];
NSString *className = NSStringFromClass(self.class);
if ([filter containsObject:className]) return ; //如果该Controller在不允许发送log的列表里,则不能继续往下走
if ([self.title isKindOfClass:[NSString class]] && self.title.length > 0){ //有标题的才符合我的要求
// 这里发送log
}
}
嗯,刚刚说到有部分Controller我是不发数据的,这里有两重判断,一个是加入到黑名单,另一个是 判断Controller的title属性是否为空
以上判断基本能满足我这个行为分析统计系统的需求,若还需要什么判断还可以继续加
以此 我只需要往工程里面添加这个Category,这个viewDidAppear就会被hook出来,可以为所欲为..
另外 需求中还提到 需要在应用启动的时候发送一次init消息
hook?可以,但我更倾向与利用category+NSNotification,因为系统中已经有 UIApplicationDidFinishLaunchingNotification
这种通知,直接用就可以
@implementation UIApplication (GBHTracer)
+ (void)load{
[super load];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ //只执行一次就可以了
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(__gbh_tracer_applicationDidFinishLaunching:) name:UIApplicationDidFinishLaunchingNotification object:nil];
});
}
+ (void)__gbh_tracer_applicationDidFinishLaunching:(NSNotification
*)noti{
//应用启动时为所欲为!
}