runtime的基本api使用

 什么是runtime? 平时项目中有使用过么?


 1.  oc是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行

 2. oc的动态性就是runtime来支撑和实现的,runtime是一套c语言的api,封装了很多动态性相关的api

 3.平时编写的oc代码,底层都是转换成了runtime api进行调用



 具体应用

 1.利用关联对象associateObject给分类添加属性

 2.遍历类的所有成员变量(获取系统控件的ivar,字典转模型,自动归档解档)

 3.交换方法实现(交换系统的方法实现)



   一 、 获取ISA指向的class对象;

            object_getClass(per);

   二 、 设置ISA指向的class;

            object_setClass(per, [lzhCarclass]);

            [persetValue:@5forKey:@"_b"];

            NSNumber*f = [pervalueForKey:@"_f"];

            [perrun];

     三、判断一个类是否是类对象;

            object_isClass(per);  //0

            object_isClass([perclass]);// 1

    四、动态创建一个类;

    1.      ClassnewClass =  objc_allocateClassPair([NSObjectclass],"DOG",0);

          实际是上面函数是创建一对类(元类和类);

           参数 : 父类  类名;  返回值:  类对象  

    2.        class_addIvar(newClass,"_age",4,1,@encode(int));

            类名  该变量占用多少字节  对齐是1个长度  类型的type;

            class_addIvar(newClass,"_wight",4,1,@encode(int));

    3.        class_addMethod(newClass,@selector(run), (IMP)run,"V@:");

    4.    注册一个类 ;相等于创建这个类的结构体;所以添加类的成员变量一定放到注册函数之前;因为注册完成以后类的结构体已经生成出来,而且成员变量是在ro_t里面只读的属性,而方法可以放到注册类后面写,因为方法是放到了rw_t里面,可以读可以写的,所以方法可以放到注册函数后面;已经注册的类不可以添加成员变量;

            objc_registerClassPair(newClass);

  5.创建完成以后的实际使用如下:

         id  dog = [[newClassalloc]init];

            [dogsetValue:@10forKey:@"_age"];

            [dogsetValue:@20forKey:@"_wight"];

            [dogrun];

            NSLog(@"%@,%@",[dogvalueForKey:@"_age"],[dogvalueForKey:@"_wight"]);

            NSLog(@"%zd",class_getInstanceSize(newClass));

   五、获取成员变量信息

            IvarageIvar =  class_getInstanceVariable([lzhPersonclass],"_age");

            NSLog(@"----%s    %s",ivar_getName(ageIvar),ivar_getTypeEncoding(ageIvar));

    //    设置成员变量的值;int 类型设置number类型是不行

            lzhPerson*person = [lzhPersonnew];

            object_setIvar(person, ageIvar,@"1234");

            NSLog(@"-----%@",person.age);

 六:     copy成员变量列表;获取成员变量列表list:使用场景比较多;

            unsignedintcount;//获取成员变量数量

           Ivar* ivars =class_copyIvarList([lzhPersonclass], &count);

            for(inti =0; i

                Ivarivar = ivars[i];

                NSLog(@"======  %s  %s",ivar_getName(ivar),ivar_getTypeEncoding(ivar));

            }

            free(ivars);//使用完成以后需要释放ivars的数组对象了;

   实际使用场景如字段转模型:

    +(instancetype)lzh_objcetWithJson:(NSDictionary *)json{


    id obj = [[selfalloc]init];

    unsignedintcount;

    Ivar*ivars =class_copyIvarList([objclass], &count);

    for(inti =0; i

//    取出来i位置的成员变量

        Ivarivar = ivars[i];

        NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];

        [namedeleteCharactersInRange:NSMakeRange(0, 1)];

//    设置

        [objsetValue:json[name]forKey:name];

    }

    free(ivars);

    returnobj;

}

七、替换方法实现

    lzhPerson*person = [lzhPersonnew];

//  参数1;如果替换实例方法传入的是类方法,如果替换类方法传入的是元类方法;

    class_replaceMethod([lzhPerson class], @selector(run), (IMP)myRun, "v");

    [personrun];

    class_replaceMethod([lzhPerson class], @selector(run), imp_implementationWithBlock(^{

        NSLog(@"1234");

    }),"v");

八:方法交换

  交换2个方法的实现;交换的是rw_t里面的array_t的methodlist的monthed_t里面的imp;

    lzhPerson*person = [lzhPersonnew];

    MethodrunMenthod =class_getInstanceMethod([lzhPersonclass],@selector(run));

    MethodtestMenthod =class_getInstanceMethod([lzhCarclass],@selector(test));


    method_exchangeImplementations(runMenthod, testMenthod);

    [personrun];

例如:拦截系统所有的按钮的点击事件:

+(void)load

{

    Methodmethod1 =class_getInstanceMethod(self,@selector(sendAction:to:forEvent:));

    Methodmethod2 =class_getInstanceMethod(self,@selector(lzh_sendAction:to:forEvent:));

    method_exchangeImplementations(method1, method2);

//    交换的是rw_t里面的array_t的methodlist的monthed_t里面的imp;


}

- (void)lzh_sendAction:(SEL)actionto:(nullableid)targetforEvent:(nullableUIEvent*)event{

    NSLog(@"%@ --- %@-------%@",self,target,NSStringFromSelector(action));

    if ([self isKindOfClass:[UIButton class]]) {

//        [target performSelector:action];

//        [self sendAction:action to:target forEvent:event];

        [selflzh_sendAction:actionto:targetforEvent:event];

    }

}

例如:拦截数组添加防止添加nil闪退问题:

   +(void)load

{

//  防止load函数会调用2次;

    staticdispatch_once_tonceToken;

    dispatch_once(&onceToken, ^{

        //    类簇 :NSString;NSArray; NSMutableArray;真正的类型并不是看到的类型;

        Classclass =NSClassFromString(@"__NSArrayM");

        Methodmethod1 =class_getInstanceMethod(class,@selector(insertObject:atIndex:));

        Methodmethod2 =class_getInstanceMethod(class,@selector(lzh_insertObject:atIndex:));

        method_exchangeImplementations(method1, method2);

        //    交换的是rw_t里面的array_t的methodlist的monthed_t里面的imp;

    });

}

- (void)lzh_insertObject:(id)anObjectatIndex:(NSUInteger)index{

    if(anObject ==nil) {

        return;

    }

    [selflzh_insertObject:anObjectatIndex:index];

    dispatch_async(dispatch_get_main_queue(), ^{

        if([anObjectisKindOfClass:NSString.class] && [anObjectisEqualToString:@"jack11111111"]) {

            NSLog(@"Extension");

        }

    });

}

例如:拦截字典添加防止添加nil为key的闪退问题:

   NSMutableDictionary * dic = [[NSMutableDictionary alloc]init];

    dic[@"name"] = @"jack";

    dic[obj] = obj;

    NSLog(@"%@",dic);

//  底层调用的是下面的方法;

//    设置值

//    dic setObject:<#(nullable id)#> forKeyedSubscript:<#(nonnull id)#>

//  取出来的值

//    dic objectForKeyedSubscript:<#(nonnull id)#>

你可能感兴趣的:(runtime的基本api使用)