美文网首页
Runtime动态添加方法

Runtime动态添加方法

作者: Dylan_kuang | 来源:发表于2019-02-11 17:35 被阅读2次

    // 2019开工第一天,今年想好好学习;看了下Runtime,然后简单记录下,怕自己忘记了:

    // 添加方法
    BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
    // 获取实例方法
    Method class_getInstanceMethod ( Class cls, SEL name );
    // 获取类方法
    Method class_getClassMethod ( Class cls, SEL name );
    // 获取所有方法的List
    Method * class_copyMethodList ( Class cls, unsigned int *outCount );
    // 替代方法的实现
    IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
    // 返回方法的具体实现
    IMP class_getMethodImplementation ( Class cls, SEL name );
    IMP class_getMethodImplementation_stret ( Class cls, SEL name );
    // 类实例是否响应指定的selector
    BOOL class_respondsToSelector ( Class cls, SEL sel );
    ⚠️:当判断一个实例方法是否实现时,第一个参数要用类对象,也就是[Person class]。
    当判断一个类方法是否实现时,第一个参数要传元类,也就是object_getClass([Person class])。
    
    

    objc_msgSend 动态添加方法

    申明一个ViewController类和Person类

    在person类中实现方法

    /**
     无参数无返回值
     */
    -(void)fun
    {
        NSLog(@"fun");
    }
    
    /**
     有参数无返回值
    
     @param food food
     @param some some
     */
    -(void)eat:(NSString *)food say:(NSString *)some
    {
        NSLog(@"%@ %@",food, some);
        
    }
    
    /**
     有参数有返回值
    
     @param value value
     @return NSString
     */
    - (NSString *)getString:(NSString *)value{
        
        NSLog(@"ba\\value = %@",value);
        
        return value;
    }
    
    

    然后在VC中对应实现相应的方法:

        //  无参数无返回值
        ((void (*)(id, SEL))objc_msgSend)(p, @selector(fun)); //正确写法
      
        //  有参数无返回值
        void (*glt_msgsend)(id, SEL, NSString *, NSString *) = (void (*)(id, SEL, NSString *, NSString *))objc_msgSend;
        glt_msgsend(p, @selector(eat:say:), @"123", @"456");
      
       //  有参数有返回值
        NSString* (*String_msgsend)(id, SEL, NSString  *) = (NSString * (*)(id, SEL, NSString *))objc_msgSend; 
        String_msgsend(p, @selector(getString:), @"value");
    
    

    class_addMethod 使用

       /*
         1.cls 哪个类
         2.SEL
         3.IMP
         4.返回值类型!
         */
    class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
    
    

    如果知道对应的函数返回值和类型,则可以直接使用

    // i:返回值类型int,若是v则表示void

    class_addMethod(self, sel, (IMP)haha, "v@:@");

    void haha(id obj,SEL sel,NSString * objc){
        NSLog(@"%@--%@--%@",obj,NSStringFromSelector(sel),objc);
    }
    
    

    否则可以用下面的方法:

      SEL swizzledMethod = @selector(systemMethod_PrintLog);
      Method swizzledMethod = class_getInstanceMethod(class, originalSelector);
      class_addMethod(class,
                            originalSelector,
                            method_getImplementation(swizzledMethod),
                            method_getTypeEncoding(swizzledMethod));
    
    

    附加:方法的替换

    load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。load的调用会引起initialize的调用。

    /**
     消息替换方法
     */
    + (void)load {
        NSLog(@"load");
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Class class = [self class];
            
            SEL originalSelector = @selector(systemMethod_PrintLog);
            SEL swizzledSelector = @selector(ll_imageName);
            
            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);
            }
        });
        
    }
    
    +(void)initialize{
        
         NSLog(@"initialize");
        
    }
    
    

    当一个methad没实现可能会引起Crash时候,方法的拦截

    //是为类方法进行决议。class_addMethod添加Method
    + (BOOL)resolveClassMethod:(SEL)name;  
    // 是为对象方法进行决议,
    + (BOOL)resolveInstanceMethod:(SEL)name;  
    
    eg1:
    
    //如果该类接收到一个没有实现的实例方法,就会来到这里
    
    +(BOOL)resolveInstanceMethod:(SEL)sel
    {
    //    NSLog(@"%@",NSStringFromSelector(sel));
        //动态添加一个方法!!
        /*
         1.cls 哪个类
         2.SEL
         3.IMP
         4.返回值类型!
         */
        class_addMethod(self, sel, (IMP)haha, "v@:@");
        
        return [super resolveInstanceMethod:sel];
    }
    
    void haha(id obj,SEL sel,NSString * objc){
        NSLog(@"%@--%@--%@",obj,NSStringFromSelector(sel),objc);
    }
    
    eg2:
    - (void) run {
        NSLog(@"%s", __func__);
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        NSLog(@"%s", __func__);
        
    //    // 元类
    //    // 实例对象、类对象、元类对象
    //    if (sel == @selector(walk)) {
    //        return class_addMethod(self, sel, (IMP)walk, "v@:");
    //    }
        
        if (sel == @selector(walk)) {
            Method runMethod = class_getInstanceMethod(self, @selector(run));
            IMP runIMP = method_getImplementation(runMethod);
            const char* types = method_getTypeEncoding(runMethod);
            NSLog(@"%s", types);
            return class_addMethod(self, sel, runIMP, types);
        }
        
        return [super resolveInstanceMethod:sel];
    }
    
    eg3:
    + (void) run {
        NSLog(@"%s", __func__);
    }
    
    + (BOOL)resolveClassMethod:(SEL)sel {
        
        if (sel == @selector(walk)) {
            Method runMethod = class_getClassMethod(self, @selector(run));
            IMP runIMP = method_getImplementation(runMethod);
            const char* types = method_getTypeEncoding(runMethod);
            NSLog(@"%s", types);
            return class_addMethod(object_getClass(self), sel, runIMP, types);
        }
        return [super resolveClassMethod:sel];
    }
    
    

    参考链接:
    Runtime基础使用场景-拦截替换方法
    class_addMethod以及消息转发机制
    objc_msgSend的使用注意事项

    相关文章

      网友评论

          本文标题:Runtime动态添加方法

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