zoukankan      html  css  js  c++  java
  • 初探runtime

    1 简介

      runtime,也叫它运行时系统。它是用c写的一套API,oc代码底层实现全都依赖它。我们说它是运行时,是相比编译,在程序编译完成之后,一些对象可通过runtime来干一些在编译时看似不可能的事。比如,动态添加一个属性,动态添加一个方法,交换两个方法之类的,还有很多能力。可以说,runtime是幕后黑手之一(没有贬义)。举个例子,让你来感受一下runtime。

      首先,创建一个对象Test *a = [[Test alloc] init];然后调用它的方法run,如下图:

      从代码中看,好像是对象a去调用方法run,不过这样理解也可以,我这里想说的是,在终端使用命令clang -rewrite-objc main.m生成main.cpp文件,里面有下列代码,如图:

      我们可以看到[a run]转换成了objc_msgSend(a, sel_registerName("run"));也就是说,对象a调用方法run,实际上是runtime调用了一个方法,参数就是对象和方法名,如果方法需要参数,会跟在后面,可以自己去试试。那么我们可以这样理解,所有的对象调用方法,最终都被转换成了objc_msgSend(id, sel)。

    2 几个概念

    2.1 isa

            在oc里面有一个叫isa的指针,它指向元类,元类的isa又会指向根元类,最终指向了NSObject的元类,这样形成一个环。所谓元类,就是类对象,例如类A,实例化出一个对象a,那么对象a的isa指针就是指向A,类A本身也是一个对象,叫类对象。如下图所示:

           我们打开objc/runtime.h,可以看到class的定义:

    struct objc_class {
      Class isa OBJC_ISA_AVAILABILITY;
    #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE;         // 父类
      const char *name OBJC2_UNAVAILABLE;                      // 类名
      long version OBJC2_UNAVAILABLE;                          // 类的版本信息,默认为0
      long info OBJC2_UNAVAILABLE;                             // 类信息,供运行期使用的一些位标识
      long instance_size OBJC2_UNAVAILABLE;                    // 该类的实例变量大小
      struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;          // 该类的成员变量链表
      struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表
      struct objc_cache *cache OBJC2_UNAVAILABLE;              // 方法缓存
      struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;  // 协议链表
    #endif } OBJC2_UNAVAILABLE;

      从上述代码中可以看到class的本质,它是一个含有isa指针的结构体,里面有父类、类名、方法列表、成员变量列表等变量。也就是说通过runtime我们可以知道该对象拥有的方法列表和成员变量列表,runtime提供了相应的api,这里就不赘述了。

    2.2 SEL(方法选择器)

      sel是方法编号,不同于函数指针,它只能通过方法名找到该方法的指针。对象内部有一个方法列表,sel通过方法名可以找到方法列表中的方法,这也是对象内部不能有同名函数的原因。下面举几个例子:

      (1)通过sel进行方法选择器

    SEL sel = NULL;
    switch (count) {
           case 0:
                sel = @selector(dismiss);
                break;
           case 1:
                sel = @selector(start);
           default:
                sel = @selector(stop);
                break;
    }

      (2)延迟加载某个方法

          [self performSelector:@selector(dismiss) withObject:nil afterDelay:2.0];2秒钟之后调用方法dismiss。

    2.3 IMP(函数指针)

       如果你没有忘记曾经被c支配的恐惧的话,应该对函数指针这个概念印象深刻,IMP就是函数指针。你可以这样理解,你能找到某个函数的指针,那么拥有该函数的对象就可以直接去调用了,而不需要通过方法名去调用。但是函数指针也是需要通过方法名去获取的,这里可以把IMP和SEL结合起来,看看下面的例子。

      (1)找到当前对象的方法log的函数指针后,直接调用。同时,也可以找其他对象内部的函数指针,然后直接调用:

    IMP imp = [self methodForSelector:@selector(log)];
    imp();

      (2)交换方法。当viewcontroller的子类调用viewWillAppear:时会变成调用方法logViewWillAppear:从而达到交换方法的目的。

    + (void)load{
        Method method1 = class_getInstanceMethod([self class], @selector(viewWillAppear:));
        Method method2 = class_getInstanceMethod([self class], @selector(logViewWillAppear:));
        method_exchangeImplementations(method1, method2);
    }
    
    - (void)logViewWillAppear:(BOOL)animated{
        NSString *className = NSStringFromClass([self class]);
        if ([className hasPrefix:@"LMF"]) {
            NSLog(@"%@ will appear", className);
        }
        [self logViewWillAppear:animated];
    }

    2.4 Method

      在上面提到了Method,这里我们来看看Method到底是什么。首先看看runtime.h文件中的定义:

    struct objc_method {
        SEL method_name   
        char *method_types
        IMP method_imp    
    }                     

      可以看出,它是一个结构体,包含SEL和IMP,实际上相当于在SEL和IMP之间作了一个映射。有了SEL,我们便可以找到对应的IMP,从而调用方法的实现代码。

      以上是初次研究runtime的一些心得,还有如何使用runtime和runtime使用场景等模块会在后续文章中讲到。

  • 相关阅读:
    查看eclipse web项目中jsp编译后的servlet源文件【转】【JSP】
    综合实战--文件上传系统【JDBC&IO&Socket】
    002、使用webpack的各种loader处理文件
    001、node & webpack工程手动搭建
    000、GO之特别语法糖
    000、GO之深刻理解拷贝
    000、常见算法解析
    003、GO之并发
    002、GO之反射
    001、GO之指针转换
  • 原文地址:https://www.cnblogs.com/lmfboke/p/7256505.html
Copyright © 2011-2022 走看看