runtime(1)概念和原理

416 阅读6分钟

一、概念

Obj-C是一门动态语言。在编一阶段并不知道变量的具体类型,也不知道具体调用的哪个函数。只有在运行时才检查变量的数据类型,同时在运行时才根据函数名查找要调用的具体函数。

Obj-C把一些决定性的工作从编译、链接阶段推迟到运行时阶段的机制使得Obcj-C更加灵活。

runtime就是Obj-C实现动态化的基础。

二、消息机制的基本原理

1、编译阶段:[receiver selector]; 方法被编译器转换为

  • objc_msgSend(receiver,selector) (不带参数)
  • objc_msgSend(recevier,selector,org1,org2,…)(带参数)

2、运行时阶段:消息接受者 receiver 寻找对应的 selector

  • 1、通过receiverisa 指针找到receiver对应的 class
  • 2、在 classcache 的列表中找到对应的 IMP
  • 3、如果在 cache 中没有找到对应的 IMP,就继续在 classmethod list中寻找,如果找到就填充到cache中并返回selector;
  • 4、如果再class中没有找到这个 selector 就继续在它的 superClass 寻找
  • 5、如果找不到 selector ,消息被转发或者临时向 receiver 添加这个 selector对应的实现方法,否则就会崩溃。(后边会详细说下转发)

三、基础概念说明

3.1、class

objc/runtime.h 中,class被定义为指向 objc_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;

上边是老的(但应该是常规考察用的比较多),新的实现如下


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() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        assert(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }

    void clearInfo(uint32_t clear) {
        assert(isFuture()  ||  isRealized());
        data()->clearFlags(clear);
    }

    // set and clear must not overlap
    void changeInfo(uint32_t set, uint32_t clear) {
        assert(isFuture()  ||  isRealized());
        assert((set & clear) == 0);
        data()->changeFlags(set, clear);
    }

    bool hasCustomRR() {
        return ! bits.hasDefaultRR();
    }
    void setHasDefaultRR() {
        assert(isInitializing());
        bits.setHasDefaultRR();
    }
    void setHasCustomRR(bool inherited = false);
    void printCustomRR(bool inherited);

    bool hasCustomAWZ() {
        return ! bits.hasDefaultAWZ();
    }
    void setHasDefaultAWZ() {
        assert(isInitializing());
        bits.setHasDefaultAWZ();
    }
    void setHasCustomAWZ(bool inherited = false);
    void printCustomAWZ(bool inherited);

    bool requiresRawIsa() {
        return bits.requiresRawIsa();
    }
    void setRequiresRawIsa(bool inherited = false);
    void printRequiresRawIsa(bool inherited);

    bool canAllocIndexed() {
        assert(!isFuture());
        return !requiresRawIsa();
    }
    bool canAllocFast() {
        assert(!isFuture());
        return bits.canAllocFast();
    }


    bool hasCxxCtor() {
        // addSubclass() propagates this flag from the superclass.
        assert(isRealized());
        return bits.hasCxxCtor();
    }
    void setHasCxxCtor() { 
        bits.setHasCxxCtor();
    }

    bool hasCxxDtor() {
        // addSubclass() propagates this flag from the superclass.
        assert(isRealized());
        return bits.hasCxxDtor();
    }
    void setHasCxxDtor() { 
        bits.setHasCxxDtor();
    }


    bool isSwift() {
        return bits.isSwift();
    }


#if SUPPORT_NONPOINTER_ISA
    // Tracked in non-pointer isas; not tracked otherwise
#else
    bool instancesHaveAssociatedObjects() {
        // this may be an unrealized future class in the CF-bridged case
        assert(isFuture()  ||  isRealized());
        return data()->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS;
    }

    void setInstancesHaveAssociatedObjects() {
        // this may be an unrealized future class in the CF-bridged case
        assert(isFuture()  ||  isRealized());
        setInfo(RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS);
    }
#endif

    bool shouldGrowCache() {
        return true;
    }

    void setShouldGrowCache(bool) {
        // fixme good or bad for memory use?
    }

    bool shouldFinalizeOnMainThread() {
        // finishInitializing() propagates this flag from the superclass.
        assert(isRealized());
        return data()->flags & RW_FINALIZE_ON_MAIN_THREAD;
    }

    void setShouldFinalizeOnMainThread() {
        assert(isRealized());
        setInfo(RW_FINALIZE_ON_MAIN_THREAD);
    }

    bool isInitializing() {
        return getMeta()->data()->flags & RW_INITIALIZING;
    }

    void setInitializing() {
        assert(!isMetaClass());
        ISA()->setInfo(RW_INITIALIZING);
    }

    bool isInitialized() {
        return getMeta()->data()->flags & RW_INITIALIZED;
    }

    void setInitialized();

    bool isLoadable() {
        assert(isRealized());
        return true;  // any class registered for +load is definitely loadable
    }

    IMP getLoadMethod();

    // Locking: To prevent concurrent realization, hold runtimeLock.
    bool isRealized() {
        return data()->flags & RW_REALIZED;
    }

    // Returns true if this is an unrealized future class.
    // Locking: To prevent concurrent realization, hold runtimeLock.
    bool isFuture() { 
        return data()->flags & RW_FUTURE;
    }

    bool isMetaClass() {
        assert(this);
        assert(isRealized());
        return data()->ro->flags & RO_META;
    }

    // NOT identical to this->ISA when this is a metaclass
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }

    bool isRootClass() {
        return superclass == nil;
    }
    bool isRootMetaclass() {
        return ISA() == (Class)this;
    }

    const char *mangledName() { 
        // fixme can't assert locks here
        assert(this);

        if (isRealized()  ||  isFuture()) {
            return data()->ro->name;
        } else {
            return ((const class_ro_t *)data())->name;
        }
    }
    
    const char *demangledName(bool realize = false);
    const char *nameForLogging();

    // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        return data()->ro->instanceSize;
    }

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }

    void setInstanceSize(uint32_t newSize) {
        assert(isRealized());
        if (newSize != data()->ro->instanceSize) {
            assert(data()->flags & RW_COPIED_RO);
            *const_cast<uint32_t *>(&data()->ro->instanceSize) = newSize;
        }
        bits.setFastInstanceSize(newSize);
    }
};

从中可以看出,在老的写法中 objc_class定义了很多变量:

ivars               自身的所有实例变量    
methodLists         方法列表    
protocols           协议列表
...

等等。。。 objc_class 结构体 存放的数据称为 元数据(metadata)
objc_class 结构体 的第一个成员变量是 isa 指针,isa 指针 保存的是所属类的结构体的实例的指针,这里保存的就是 objc_class 结构体的实例指针,而实例换个名字就是 对象。换句话说,Class(类) 的本质其实就是一个对象,我们称之为 类对象。
新的runtime把方法列表,变量放到了 class_rw_t

3.2、Object

Object的源码如下

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

id被定义为一个指向objc_object的指针,从源码中可以看到objc_object只包含一个Class类型的isa指针。
当我们对一个对象进行方法调用的时候,他会通过 objc_objectisa指针去找对应的objc_class,然后在 objc_class中找到对应的方法、实例变量等。

3.3、Meta Class

Meta Class(元类) 就是一个类对象所属的 类。一个对象所属的类叫做 类对象,而一个类对象所属的类就叫做 元类。

Runtime 中把类对象所属类型就叫做 Meta Class(元类),用于描述类对象本身所具有的特征,而在元类的 methodLists 中,保存了类的方法链表,即所谓的「类方法」。并且类对象中的 isa 指针 指向的就是元类。每个类对象有且仅有一个与之相关的元类。

类方法的调用过程 和对象方法调用差不多,流程如下:

  • 1、通过类对象 isa 指针 找到所属的 Meta Class(元类);
  • 2、在 Meta Class(元类) 的 method list(方法列表) 中找到对应的 selector;
  • 3、执行对应的 selector。
3.4、实例对象、类、元类之间的关系

上图就是三者之间的关系。

isa 指针:

  • 1、水平方向上,每一级中的 实例对象 的 isa 指针 指向了对应的 类对象,而 类对象 的 isa 指针 指向了对应的 元类。而所有元类的 isa 指针 最终指向了 NSObject 元类,因此 NSObject 元类 也被称为 根元类。
  • 2、垂直方向上, 元类 的 isa 指针 和 父类元类 的 isa 指针 都指向了 根元类。而 根元类 的 isa 指针 又指向了自己。
3.5、Method

object_class 结构体 的 methodLists(方法列表)中存放的元素就是 Method(方法)。
源码如下

///这是老的写法,从后边注释可以看到
struct objc_method {
    SEL method_name     OBJC2_UNAVAILABLE;
    char *method_types  OBJC2_UNAVAILABLE;
    IMP method_imp      OBJC2_UNAVAILABLE;
} 

新的写法如下

struct method_t {
    SEL name;
    const char *types;
    IMP imp;

    struct SortBySELAddress :
        public std::binary_function<const method_t&,
                                    const method_t&, bool>
    {
        bool operator() (const method_t& lhs,
                         const method_t& rhs)
        { return lhs.name < rhs.name; }
    };
};

结构变化不大,多了个排序的结构体。

可以看到,不管新旧写法,其中都包含了 method_name(方法名),method_types(方法类型) 和 method_imp(方法实现)。

1、SEL method_name; // 方法名

/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;

SEL是一个指向objc_selector的指针, SEL的内容为存储方法名的字符串

2、IMP method_imp; // 方法实现

/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id (*IMP)(id, SEL, ...); 
#endif

IMP 的实质是一个函数指针,所指向的就是方法的实现。IMP用来找到函数地址,然后执行函数。

3、char *method_types;

方法类型 method_types 是个字符串,用来存储方法的参数类型和返回值类型。

runtime的概念和基础知识大概如上,接下来会写runtime的机制以及实际应用。