美文网首页
runtime详解

runtime详解

作者: 雪域红鹰 | 来源:发表于2020-04-17 23:30 被阅读0次

    什么是runtime

    runtime即运行时,它是一个库.这个库是c、c++、汇编语言编写的,提供的API基本都是c语言的.正是由于这个库的存在,才使得oc具备了面向对象的能力,也使得oc成为了一门动态语言.对于C语言,函数的调用在编译的时候会决定调用哪个函数.对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用.

    和运行时系统的交互

    Objective-C 程序有三种途径和运行时系统交互:
    1.通过 Objective-C 源代码;
    2.通过 Foundation 框架中类 NSObject 的方法;
    3.通过直接调用运行时系统的函数@selector

    runtime本质

    runtime本质就是对象发送消息(objc_msgsend).也就是说任何方法的调用,都是转化为消息机制.

            Person *p = [[Person alloc] init];
            [p test];
    

    上面方法的调用 [p test],会被编辑器转弯runtime库中的objc_megsend调用的方式来执行,即:
    objc_msgSend(p,sel_registerName("test"))

    第一步:对象通过isa指针找到它所继承的类class;
    第二步:在class的method_list中查找对应的方法;
    第三步:如果未查找到当前方法,会向superclass类中查找,直到找到当前调用的方法


    方法查找流程.png

    如果每次调用方法都需要遍历,系统消耗比较大,因此需要对常用的方法做缓存操作,每次查找先找缓存,就可以避免大量的无效操作。
    每一个对象都存在一个isa指针,指向对象的类,类也是一个对象也存在一个isa指针指向元类,元类指向根元类,根元类指向自己。类中保存所有的实列方法,元类保存了所有类方法。方法查找过程:


    isa走位流程.png

    runtime消息转发

    使用对象调用一个方法时,系统会查看这个对象是否能够接受到这个消息,如果接受不到一般情况会崩溃,在崩溃之前会依次调用一下几个方法
    1、解决实列方法和类方法

      +(BOOL)resolveInstanceMethod:(SEL)sel;    
      +(BOOL)resolveClassMethod:(SEL)sel;
    

    2、消息转发

      -(id)forwardingTargetForSelector:(SEL)aSelector;
    

    3、消息签名

      -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
      -(void)forwardInvocation:(NSInvocation *)anInvocation;
    

    方法的执行流程


    消息转发流程.png

    因此在崩溃之前,我们可以在以上几个方法中处理解决崩溃问题

     +(BOOL)resolveInstanceMethod:(SEL)sel{
       NSLog(@"resolveInstanceMethod:%@",NSStringFromSelector(sel));
       if (![self respondsToSelector:sel]) {
           class_addMethod(self.class, sel, (IMP)noMethod, "");
           return YES;
       }
        return [super resolveInstanceMethod:sel];
     }
    void noMethod(Class cls,SEL _cmd){
         NSLog(@"我是一个没实现的方法");
     }
    

    runtime实际应用

    1.方法交换

     + (void)load {
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
               //原本调用viewWillAppear
               Method m1 = class_getInstanceMethod(self,        @selector(viewWillAppear:));
               //现在调用新的方法
               Method m2 = class_getInstanceMethod(self, @selector(new_viewWillAppear:));
               //runtime的2个方法交换
               method_exchangeImplementations(m1, m2);
            });
           }
    
       - (void)new_viewWillAppear:(BOOL) animated {
           NSLog(@"%s", __func__);
           //这个方法其实就是调用viewWillAppear
           [self new_viewWillAppear:animated];
         }
    

    2.动态获取对象的属性,私有变量,修改属性值

     / /获取当前类的所有方法名称和个数
     unsigned int methodCount = 0;
     Method *methodList = class_copyMethodList([self class], &methodCount);
     NSLog(@"%d",methodCount);
     NSMutableArray *methodArray = [NSMutableArray arrayWithCapacity:methodCount];
     for (int i=0; i<methodCount; i++) {
         Method temp = methodList[i];
         SEL name = method_getName(temp);
         const char *name_s = sel_getName(name);
         NSLog(@"name:%s",name_s);
     }
    

    打印结果:

    8
    name:person1
    name:person2
    name:setPerson1:
    name:setPerson2:
    name:.cxx_destruct
    name:observeValueForKeyPath:ofObject:change:context:
    name:touchesBegan:withEvent:
    name:viewDidLoad
    

    获取属性并修改属性值

     unsigned int count = 0;
     Ivar *ivarList = class_copyIvarList(self.class, &count);
     for (int i=0; i<count; i++) {
        Ivar ivar = ivarList[i];
        const char *ivarAddresss = ivar_getName(ivar);
        const char *ivarType = ivar_getTypeEncoding(ivar);
        NSString *address = [NSString stringWithUTF8String:ivarAddresss];
        NSLog(@"%d :ivarAddresss:%s  ivarType:%s",i,ivarAddresss,ivarType);
        //修改属性值
        if ([address isEqualToString:@"var_name"]) {
            object_setIvar(self, ivar, @"123456");
        }
     }
     NSLog(@"var_name:%@",var_name);
    

    打印结果:

      0 :ivarAddresss:var_name  ivarType:@"NSString"
      var_name:123456
    

    3.给分类添加属性

      1、给对象关联一个属性
      objc_setAssociatedObject(array, &array_key, dic,     OBJC_ASSOCIATION_RETAIN_NONATOMIC);
      2、通过绑定key获取关联对象
      NSDictionary *di = objc_getAssociatedObject(array, &array_key);
      3、移除关联对象
      objc_setAssociatedObject(array, &array_key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
      4、移除所有关联对象
      objc_removeAssociatedObjects(array);
    

    eg:

      #import <UIKit/UIKit.h>
      NS_ASSUME_NONNULL_BEGIN
    
      @interface UIButton (test)
    
      @property (nonatomic,strong) NSString *addrees;
    
      @end
    
      #import "UIButton+test.h"
      #import <objc/runtime.h>
    
      const NSString *button_category_addrees = @"button_category_addrees";
    
      @implementation UIButton (test)
    
       -(void)setAddrees:(NSString *)ids{
              objc_setAssociatedObject(self, &button_category_addrees, ids,   OBJC_ASSOCIATION_RETAIN_NONATOMIC);
       }
    
       -(NSString *)addrees{
        return objc_getAssociatedObject(self, &button_category_addrees);
      }
    @end
    

    代码调用:

       UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
       button.frame = CGRectMake((self.view.frame.size.width-100)/2, 200, 100, 40);
       button.backgroundColor = [UIColor redColor];
       button.addrees = @"陕西西安市雁塔区科技路";
       [button setTitle:@"点击" forState:UIControlStateNormal];
       [button addTarget:self action:@selector(btnEvent:) forControlEvents:UIControlEventTouchUpInside];
      [self.view addSubview:button];
      
      -(void)btnEvent:(UIButton *)btn{
          NSLog(@"打印UIButton添加address属性值是:%@",btn.addrees);
        }
    

    打印结果:

      打印UIButton添加address属性值是:陕西西安市雁塔区科技路
    

    相关文章

      网友评论

          本文标题:runtime详解

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