美文网首页
runtime常用方法

runtime常用方法

作者: 冰点雨 | 来源:发表于2021-01-18 14:23 被阅读0次

    1.类

    //动态创建一个类(参数:父类,类名,额外的内存空间)
    Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
     
    //注册一个类(要在类注册之前添加成员变量)
    void objc_registerClassPair(Class cls) 
     
    //销毁一个类
    void objc_disposeClassPair(Class cls)
     
    //获取isa指向的Class
    Class object_getClass(id obj)
     
    //设置isa指向的Class
    Class object_setClass(id obj, Class cls)
     
    //判断一个OC对象是否为Class
    BOOL object_isClass(id obj)
     
    //判断一个Class是否为元类
    BOOL class_isMetaClass(Class cls)
     
    //获取父类
    Class class_getSuperclass(Class cls)
    

    1.1 动态修改指针指向对象

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface Car : NSObject
    @property(nonatomic,copy)NSString *name;
    @property(nonatomic,assign)NSInteger age;
    - (void)run;
    @end
    
    @interface Person : NSObject
    @property(nonatomic,copy)NSString *name;
    @property(nonatomic,assign)NSInteger age;
    - (void)run;
    @end
    
    #import "Person.h"
    #import <objc/runtime.h>
    @implementation Car
    - (void)run{
        NSLog(@"%s", __func__);
    }
    @end
    
    
    @implementation Person
    - (void)run{
    //    NSLog(@"---selector:%@",NSStringFromSelector(_cmd));
        NSLog(@"%s", __func__);
        
        NSLog(@"---class:%@",object_getClass(self));
       
    //    BOOL isClass = object_isClass(self);
    //    NSLog(@"---isClass:%d",isClass);
    }
    
    @end
    
    
    - (void)testCard{
        Person *p = [[Person alloc]init];
        [p run];
        
        object_setClass(p, [Car class]);
        [p run];
    }
    

    控制台打印参数

    2021-01-18 11:24:51.757886+0800 BlockTest[71022:1903556] -[Person run]
    2021-01-18 11:24:51.758050+0800 BlockTest[71022:1903556] ---class:Person
    2021-01-18 11:24:51.758163+0800 BlockTest[71022:1903556] -[Car run]
    

    1.2 动态创建类

    - (void)testClass{
        
        Class newClass = objc_allocateClassPair([NSObject class], "Dog", 0);
        // 建议在注册类之前添加这些成员变量、属性和方法
        //(成员变量是只读的,类的结构一旦创建,就不能再添加成员变量 ro_t 方法的添加可以放在注册之后,
        // 因为方法是放在rw_t中)
        class_addIvar(newClass, "_age", 4, 1, @encode(int));
        class_addIvar(newClass, "_weight", 4, 1, @encode(int));
        SEL sel = NSSelectorFromString(@"run");
        IMP imp = [self methodForSelector:sel];
        class_addMethod(newClass, @selector(run), imp, "v@:");
        // 注册类
        objc_registerClassPair(newClass);
        
        id dog = [[newClass alloc] init];
        [dog setValue:@10 forKey:@"_age"];
        [dog setValue:@20 forKey:@"_weight"];
        [dog run];
        
        NSLog(@"age:%@  name:%@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]);
        
        // 在不需要这个类时释放
        //        objc_disposeClassPair(newClass);
        
    }
    
    - (void)run{
        NSLog(@"======00000");
    }
    
    2021-01-18 11:43:35.349399+0800 BlockTest[74008:1922892] ======00000
    2021-01-18 11:43:35.349694+0800 BlockTest[74008:1922892] age:10  name:20
    

    2.成员变量

    //获取一个实例变量信息 (获取的描述信息,并不是实际值和信息)
    Ivar class_getInstanceVariable(Class cls, const char *name)
     
    //拷贝实例变量列表(最后需要调用free释放)
    Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
     
    //设置和获取成员变量的值
    void object_setIvar(id obj, Ivar ivar, id value)
    id object_getIvar(id obj, Ivar ivar)
     
    //动态添加成员变量(已经注册的类是不能动态添加成员变量的)
    BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
     
    //获取成员变量的相关信息
    const char *ivar_getName(Ivar v)
    const char *ivar_getTypeEncoding(Ivar v)
    

    2.1 获取成员变量信息

    - (void)testIvars
    {
        // 3: 获取成员变量信息 获取的描述信息,并不是实际值和信息
        Ivar ageIvar = class_getInstanceVariable([Person class], "_age");
        NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));
        
        // 3.1: 设置和获取成员变量的值
        Ivar nameIvar = class_getInstanceVariable([Person class], "_name");
        
        Person *person = [[Person alloc] init];
        
        object_setIvar(person, nameIvar, @"xiao hua");  // 设置的name string
        object_setIvar(person, ageIvar, (__bridge id)(void *)10);
        NSLog(@"---- %@ %ld", person.name, person.age);
        
        
        // 4: 成员变量的数量  copy一个成员变量列表 最常用
        unsigned int count;
        Ivar *ivars = class_copyIvarList([Person class], &count);
        for (int i = 0; i < count; i++) {
            // 取出i位置的成员变量
            Ivar ivar = ivars[i]; // 指针可以当做数组来用,相当于是*(ivars + i) 指针跟着移动 然后取出地址
            NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
        }
        free(ivars); // 需要释放
    }
    
    2021-01-18 11:31:01.533314+0800 BlockTest[71213:1909377] _age q
    2021-01-18 11:31:01.533442+0800 BlockTest[71213:1909377] ---- xiao hua 10
    2021-01-18 11:31:01.533531+0800 BlockTest[71213:1909377] _name @"NSString"
    2021-01-18 11:31:01.533612+0800 BlockTest[71213:1909377] _age q
    

    3.属性

    //获取一个属性
    objc_property_t class_getProperty(Class cls, const char *name)
     
    //拷贝属性列表(最后需要调用free释放)
    objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
     
    //动态添加属性
    BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                      unsigned int attributeCount)
     
    //动态替换属性
    void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                          unsigned int attributeCount)
     
    //获取属性的一些信息
    const char *property_getName(objc_property_t property)
    const char *property_getAttributes(objc_property_t property)
    

    4.方法

    //获得一个实例方法、类方法
    Method class_getInstanceMethod(Class cls, SEL name)
    Method class_getClassMethod(Class cls, SEL name)
     
    //方法实现相关操作
    IMP class_getMethodImplementation(Class cls, SEL name) 
    IMP method_setImplementation(Method m, IMP imp)
    void method_exchangeImplementations(Method m1, Method m2) 
     
    //拷贝方法列表(最后需要调用free释放)
    Method *class_copyMethodList(Class cls, unsigned int *outCount)
     
    //动态添加方法
    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
     
    //动态替换方法
    IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
     
    //获取方法的相关信息(带有copy的需要调用free去释放)
    SEL method_getName(Method m)
    IMP method_getImplementation(Method m)
    const char *method_getTypeEncoding(Method m)
    unsigned int method_getNumberOfArguments(Method m)
    char *method_copyReturnType(Method m)
    char *method_copyArgumentType(Method m, unsigned int index)
     
    //选择器相关
    const char *sel_getName(SEL sel)
    SEL sel_registerName(const char *str)
     
    //用block作为方法实现
    IMP imp_implementationWithBlock(id block)
    id imp_getBlock(IMP anImp)
    BOOL imp_removeBlock(IMP anImp)
    

    4.1 交换方法

    - (void)replaceMethodTest{
        
        Person *p = [[Person alloc]init];
        // 1:替换方法:对象方法,(一般要替换一个类方法 这里需要传进去的就是元类对象,需注意)
        SEL sel = NSSelectorFromString(@"myRun");
        IMP imp = [self methodForSelector:sel];
        class_replaceMethod([Person class], @selector(run), imp, "v@:");
        
        // 2:imp_implementationWithBlock 这里传入的是block,将block包装秤了一个imp方法
    //       class_replaceMethod([Person class], @selector(run), imp_implementationWithBlock(^{
    //           NSLog(@"123123");
    //       }), "v");
    
        [p run];
    }
    
    - (void)myRun{
        NSLog(@"======myRun");
    }
    

    4.2 建分类交换方法

    //HookTool.h
    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface HookTool : NSObject
    
    + (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    
    //HookTool.m
    #import "HookTool.h"
    #import <objc/runtime.h>
    
    @implementation HookTool
    //调用该方法,在单例模式下进行  保证方法只被交换一次
    + (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector
    {
        if (cls == nil) {
            NSLog(@"传入的交换类不能为空");
            return;
        }
        Class class = cls;
        
        // 原方法结构体和替换方法结构体
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        // 如果当前类没有原方法的实现IMP,先调用class_addMethod来给原方法添加默认的方法实现IMP
        BOOL didAddMethod =
        class_addMethod(class,
                        originalSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));
        
        //要交换的方法不存在
        if (!originalMethod) {
               class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
               method_setImplementation(swizzledMethod, imp_implementationWithBlock(^(id self, SEL _cmd){ }));
           }
        if (originalMethod) {
             if (didAddMethod) {
                   // 添加成功了证明自己没有,那么直接替换
                   class_replaceMethod(class,
                                       swizzledSelector,
                                       method_getImplementation(originalMethod),
                                       method_getTypeEncoding(originalMethod));
               } else {
                   // 自己有直接交换
                   method_exchangeImplementations(originalMethod, swizzledMethod);
               }
        }
    }
    

    UIControl分类

    #import "UIControl+UserStatidtics.h"
    #import "HookTool.h"
    
    @implementation UIControl (UserStatidtics)
    +(void)load{
        static dispatch_once_t onceToken;
            _dispatch_once(&onceToken, ^{
                // 只在开发模式下,才会出现交换
                #ifdef DEBUG
    
                SEL originalSelector = @selector(sendAction:to:forEvent:);
                SEL swizzledSelector = @selector(swiz_sendAction:to:forEvent:);
                [HookTool swizzlingInClass:[self class] originalSelector:originalSelector swizzledSelector:swizzledSelector];
    
                #endif
            });
    }
    
    
    #pragma mark - Method Swizzling
    - (void)swiz_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event;
    {
        //可插入埋点代码
    
        NSLog(@"==== 交换方法");
    
        [self swiz_sendAction:action to:target forEvent:event];
    }
    @end
    
    2021-01-18 14:18:27.345827+0800 BlockTest[76175:1978395] -[UIControl(UserStatidtics) swiz_sendAction:to:forEvent:]
    2021-01-18 14:18:27.345953+0800 BlockTest[76175:1978395] ==== 交换方法
    2021-01-18 14:18:27.346053+0800 BlockTest[76175:1978395] ---btn Click
    

    参考文章:https://blog.csdn.net/qq_27909209/article/details/82699408

    相关文章

      网友评论

          本文标题:runtime常用方法

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