读 Runtime 源码:类与对象

作者: Joy___ | 来源:发表于2016-08-02 20:35 被阅读937次

    以前只是看了很多博客,这次打算看一下源码,并记录下来。想到哪里就读到哪里,写到哪里。读的代码版本是: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 源码:类与对象

      本文链接:https://www.haomeiwen.com/subject/ndfqsttx.html