Runtime是什么

由于Objc是一门动态语言,所以它总是想办法把一些决定作业从编译衔接推迟到运行时。也便是说只有编译器是不行的,还需要一个运行时体系 (runtime system) 来履行编译后的代码。这便是 Objective-C Runtime 体系存在的含义,它是整个Objc运行结构的一块柱石。

Runtime是由c、c++、汇编提供的一整套运行时功用。

Runtime相关头文件内容

ios的sdk中 usr/include/objc文件夹下面的文件

List.h
NSObjCRuntime.h
NSObject.h
Object.h
Protocol.h
a.txt
hashtable.h
hashtable2.h
message.h
module.map
objc-api.h
objc-auto.h
objc-class.h
objc-exception.h
objc-load.h
objc-runtime.h
objc-sync.h
objc.h
runtime.h

都是和运行时相关的头文件,其中首要运用的函数界说在message.h和runtime.h这两个文件中。 在message.h中首要包含了一些向目标发送音讯的函数,这是OC目标办法调用的底层完成。 runtime.h是运行时最重要的文件,其中包含了对运行时进行操作的办法。 首要包含

//runtime.h
// An opaque type that represents a method in a class definition. 一个类型,代表着类界说中的一个办法
typedef struct objc_method *Method;
/// An opaque type that represents an instance variable.代表实例(目标)的变量
typedef struct objc_ivar *Ivar;
/// An opaque type that represents a category.代表一个分类
typedef struct objc_category *Category;
/// An opaque type that represents an Objective-C declared property.代表OC声明的特点
typedef struct objc_property *objc_property_t;
// Class代表一个类,它在objc.h中这样界说的  typedef struct objc_class *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;
    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;

这些类型的界说,对一个类进行了完全的分化,将类界说或者目标的每一个部分都笼统为一个类型type,对操作一个类特点和办法十分便利。OBJC2_UNAVAILABLE符号的特点是Ojective-C 2.0不支持的,但实际上能够用呼应的函数获取这些特点,例如:假如想要获取Class的name特点,能够按如下办法获取:

Class classPerson = Person.class;
// printf("%s\n", classPerson->name); //用这种办法现已不能获取name了 由于OBJC2_UNAVAILABLE
const char *cname  = class_getName(classPerson);
printf("%s", cname); // 输出:Person

2.1 函数的界说

对目标进行操作的办法一般以object_最初

对类进行操作的办法一般以class_最初

对类或目标的办法进行操作的办法一般以method_最初

对成员变量进行操作的办法一般以ivar_最初

对特点进行操作的办法一般以property_最初

对协议进行操作的办法一般以protocol_最初

根据以上的函数的前缀 能够大致了解到层级联系。关于以objc_最初的办法,则是runtime终究的管家,能够获取内存中类的加载信息,类的列表,关联目标和关联特点等操作。

例如:运用runtime对当前的应用中加载的类进行打印,别被吓一跳。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    unsigned int count = 0;
    Class *classes = objc_copyClassList(&count);
    for (int i = 0; i < count; i++) {
        const char *cname = class_getName(classes[i]);
        printf("%s\n", cname);
    }
}

办法

首要看一下Runtime 办法查找的思想导图
runtime底层探索.png

在OC中,办法的实质便是音讯发送,objc_msgSend,经过汇编代码和经过Clang编译成c++代码,咱们能够看到在oc中的办法终究会转换

//参数为:接收器、挑选子、多参数
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

其中接收器指音讯的接收方,也便是目标,挑选子也便是该目标的SEL。

SEL和IMP

SEL:类成员的办法指针,不同于函数指针,仅仅个办法编号
IMP:函数指针,指向咱们界说的函数
假如运用书本来类比,那么SEL便是书本的目录,IMP便是书本的页码,办法函数便是该页码下的内容。

办法查找

举个比如
[obj foo] -> objc_msg(obj, foo)的查找流程如下:

  1. 经过obj的isa指针找到obj的class
  2. 在class的method list找到foo办法
  3. 假如class中没找到foo,继续往上找,superclass
  4. 一旦找到foo,履行它的完成IMP
  5. 查找不到,进入动态办法解析
  6. 解析不到,进入快速转发
  7. 快速转发,返回目标为nil,则进入完整的音讯转发流程
  8. 真实找不到,抛出反常

这种效率太慢,每次都要查找,所以引进objc_cache,找到foo,把foo的method_name作为key,method_imp(函数指针)作为value存起来,结构是哈希表。

查找流程

image.png