(一)Runtime简介

753 阅读3分钟

一、Runtime定义

Runtime(运行时)是一套由C、C++、汇编语言编写的API,他的作用是将数据类型由编译时推到了运行时,我们平时编写的OC代码,在程序运行的过程中都会被编译成Runtime的C语言代码。我们一般需要runtime来创建类和对象,进行消息传递,转发等。

二、Runtime特性

具有运行时的特性。

三、Runtime作用

  • 可以动态的创建、添加类,修改这个类的属性和方法。
  • 遍历一个类中的所有成员变量,属性,和方法。
  • 用于消息的传递和转发等。

四、对象实质

苹果官网提供的开源代码

OC中对象 id被编译成objc_object

struct objc_object {
private:
    isa_t isa;

public:
    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();
    
    // object may have associated objects?
    bool hasAssociatedObjects();
    void setHasAssociatedObjects();
    
    // object may be weakly referenced?
    bool isWeaklyReferenced();
    void setWeaklyReferenced_nolock();
    
    // Optimized calls to retain/release methods
    id retain();
    void release();
    id autorelease();
    
    bool sidetable_isWeaklyReferenced();
    void sidetable_setWeaklyReferenced_nolock();
    (粘贴一小部分)
    .......
}

里面包含很多的内容如:isa_t的共用体、isa操作、 弱引用、关联对象、内存管理等。

由此我们可以看出对象的本质是一个结构体。

五、那么Class是什么

1)找到NSObject.h

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop

我们还知道NSObject是一个Class,看一下苹果是如何对类进行定义的。

typedef struct objc_class *Class;
/// Represents an instance of a class.

由此可知Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。

那我们来看一下objc_class

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    .......
}

类是继承于objc_object的,所以我们也可以objc_class为类对象。

2)在oc中的runtime头文件中objc_class结构体中的定义如下:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

简单分析一下们长用的字段

  • isaClass对象,指向objc_class结构体的指针,也就是这个ClassMetaClass(元类)
    • 类的实例对象的 isa 指向该类;该类的 isa 指向该类的 MetaClass
    • MetaCalssisa对象指向SuperMetaCalss(根元类)
    • 如果MetaClassRootMetaCalss,那么该MetaClassisa指针指向它自己。
  • super_classClass对象指向父类对象
    • 如果该类的对象已经是RootClass,那么这个super_class指向nil
    • MetaCalssSuperClass指向父类的MetaCalss
    • MetaCalssRootMetaCalss,那么该MetaClassSuperClass指向该对象的RootClass

一张常见的isa,super_class走位流程

  • ivars: 类中所有的属性列表。使用场景:我们在字典转换成模型的时候需要用到这个列表找到属性的名称,去取字典中的值,KVC赋值,或者直接Runtime赋值
  • methodLists: 类中所有的方法列表。使用场景:如在程序中写好方法,通过外部获取到方法名称字符串,然后利用反射机制通过这个字符串得到方法,从而达到外部控制App已知方法。
  • cache:缓存调用过的方法列表。调用方法的时候,会先从缓存中查找,如果没有在去方法列表中查找,从而提高运行效率。调用过的方法都会存到缓存中。
  • protocols:该类遵循了哪些协议。

六、何时添加实例变量

能否向编译后的得到的类中增加实例变量?能否向运行时创建的类中增加实例变量?

  • 不能向编译后的类中增加实例变量,因为一旦编译完成,类中的instance_size已经确定,编译后的实例变量存储的ivars中,我们无法修改。
  • 只要还没有注册到内存中,就可以添加。
  • 可以在运行时添加属性和方法。

总结:

为什么我们总说OC是一门动态语言,因为OC对象都是基于RunTime实现的,由编译时推到了运行时。可以动态的创建、添加类,修改这个类的属性和方法。