读 Runtime 源码:类与对象

读 Runtime 源码:类与对象_第1张图片

以前只是看了很多博客,这次打算看一下源码,并记录下来。想到哪里就读到哪里,写到哪里。读的代码版本是:objc runtime 680,可以从这里下载

对象和类

首先在 objc-private.h文件中可以看到objc_object结构体,这就是对对象的定义

struct objc_object {
private:
    isa_t isa;
}

objc-runtime-new.h中可以看到objc_class结构体,这就是对类的定义,因为它继承了objc_object,所以我把isa变量也加入到objc_class中来。

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

superclass指向该类的父类,NSObject的父类指向NULL

Cache

cache主要用于方法性能优化,如果每发送一个消息都需要去方法表中去查找,当方法很多的时候,查找是很耗力的,并且当 存在继承关系的时候,一个方法的查找链可能会很长。那么对使用过的方法进行缓存,便于第二次查找,这样节省的时间也是非常可观的。

来看一下cache_t的具体代码,在objc-runtime-new.h的第52行可以看到

struct cache_t {
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
}
  • mask:分配用来缓存bucket的总数
  • occupied:表明目前实际占用的缓存bucket的个数
  • _buckets:一个散列表,用来方法缓存,bucket_t类型,包含key以及方法实现IMP
struct bucket_t {
private:
    cache_key_t _key;
    IMP _imp;
}

class_data_bits_t

objc-runtime-new.h的 817 行可以看到结构体中只有一个bits属性来存储类信息

struct class_data_bits_t {

    // Values are the FAST_ flags above.
    uintptr_t bits;
}

还有objc_class有注释说

 // class_rw_t * plus custom rr/alloc flags

那我们看一下class_rw_t到底是什么,在objc-runtime-new.h的778行可以看到

struct class_rw_t {
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
}

Objc的类的属性、方法、以及遵循的协议都放在class_rw_t中,下面对其中几个关键词进行分析

class_ro_t

ro是一个指向常量的指针,存储来编译器决定了的属性、方法和遵守协议

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

Method

Method存储了方法名、类型信息、方法实现

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

iVar

ivar代表类中的实例变量

struct ivar_t {
#if __x86_64__
    // *offset was originally 64-bit on some x86_64 platforms.
    // We read and write only 32 bits of it.
    // Some metadata provides all 64 bits. This is harmless for unsigned 
    // little-endian values.
    // Some code uses all 64 bits. class_addIvar() over-allocates the 
    // offset for their benefit.
#endif
    int32_t *offset;
    const char *name;
    const char *type;
    // alignment is sometimes -1; use alignment() instead
    uint32_t alignment_raw;
    uint32_t size;

    uint32_t alignment() const {
        if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
        return 1 << alignment_raw;
    }
};

说起来实例变量,就想到了对象的内存结构,为了搞清楚,也做了一个测试,Person继承于PeoplePeople继承于NSObject

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        // 此处打断点
        }
}

在注释处打断点,然后在 Console 中输入p *person,查看输出结果,对象中会包含一个isa指针、类的实例变量,以及父类的实例变量

(lldb) p *person
(Person) $0 = {
  People = {
    NSObject = {
      isa = Person
    }
    _name = nil
  }
  _age = nil
}
(lldb) 

如果有理解错误,望留言告知

你可能感兴趣的:(读 Runtime 源码:类与对象)