美文网首页
方法交换

方法交换

作者: StartBoy | 来源:发表于2018-05-16 10:06 被阅读0次

    +(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{
    
          //应用启动时为所欲为!
    
     }
    

    相关文章

      网友评论

          本文标题:方法交换

          本文链接:https://www.haomeiwen.com/subject/yqzudftx.html