isa - 类的底层原理结构

974 阅读5分钟

类的分析

对象的isa指向的是类,万物皆对象,类也是个对象,类里面也有isa,那么类isa是指向哪个类呢?这个类就是苹果定义的元类

元类(metaClass)

存在即合理,元类存在的意义在哪里?

int main(int argc, char * argv[]) {
    @autoreleasepool {
       Class class1 = [SWPerson class];
       Class class2 = [SWPerson alloc].class;
       Class class3 = object_getClass([SWPerson alloc]);
       Class class4 = [SWPerson alloc].class;
       NSLog(@"\n-%p-\n-%p-\n-%p-\n-%p-",class1,class2,class3,class4);
    }
    return 
}

结果:
-0x100009510-
-0x100009510-
-0x100009510-
-0x100009510-
  • 类对象的地址都是一样的,内存中每一个类只有一块内存,和普通的对象有明显的区别

用来获取类信息的掩码,将isa的地址&、ISA_MASK,可以解析得到类的信息,ISA_MASK是系统定义的,不同架构不同,我在objc4源码内拿到的 x86_64:define ISA_MASK 0x00007ffffffffff8ULL arm64:define ISA_MASK 0x0000000ffffffff8ULL arm64(simulators):define ISA_MASK 0x007ffffffffffff8ULL

  1. p/x SWPerson.class 得到类对象内存地址0x00000001000080e8,x/4gx格式化输出类对象的内存地址
  2. p/x 0x00000001000080c0 & 0x00007ffffffffff8将类对象的首地址(isa指针地址)0x00000001000080c0和ISA_MASK做与操作,得到了一个新的内存地址0x00000001000080c0
  3. po 0x00000001000080c0 打印这个地址数据,得到了SWPerson 0x00000001000080e80x00000001000080c0 是两个不同的地址,0x00000001000080e8 是类地址,而 0x00000001000080c0 地址对应的类就叫元类

小结

  • 元类是系统编译器自动创建的,和用户没关系
  • 实例对象的isa指向类,类对象的isa指向元类
  • 类名和它关联类即元类名是一样的(只有关联元类才有类名)

isa走位图分析

Apple 官方 isa走位和继承图 isa流程图.png

  • 实例对象的isa --> 类对象
  • 类对象的isa--> 元类
  • 元类isa--> 根元类
  • 根元类的isa--> 根元类(指向自己)

类 元类 根元类继承图

int main(int argc, char * argv[]) {
    @autoreleasepool {
    Class tMetaClass = object_getClass(SWTeacher.class);    //SWTeacher 的元类
    Class tMetaSuperClass = class_getSuperclass(tMetaClass);//SWTeacher 的元类的父类
    
    Class pMetaClass = object_getClass(SWPerson.class);     //SWPerson 的元类
    Class pMeatSuperClass = class_getSuperclass(pMetaClass);//SWPerson 的元类的父类
   
    Class nMetaClass = object_getClass(NSObject.class);     //NSObject的元类
    Class nSuperClass = class_getSuperclass(NSObject.class);//NSObject的父类
    Class nMetaSuperClass = class_getSuperclass(nMetaClass);//NSObject的元类的父类
    
    NSLog(@"SWTeacher-%p",SWTeacher.class);
    NSLog(@"SWPerson-%p",SWPerson.class);
    NSLog(@"NSObject-%p",NSObject.class);
 
    NSLog(@"%@ - %p - %@ - %p",tMetaClass,tMetaClass,tMetaSuperClass,tMetaSuperClass);
    NSLog(@"%@ - %p - %@ - %p",pMetaClass,pMetaClass,pMeatSuperClass,pMeatSuperClass);
    NSLog(@"%@ - %p - %@ - %p",nMetaClass,nMetaClass,nMetaSuperClass,nMetaSuperClass);
    }
    return 
}

SWTeacher-0x100009618
SWPerson-0x100009528
NSObject-0x7fff8deb3118
SWTeacher - 0x1000095f0 - SWPerson - 0x100009500
SWPerson - 0x100009500 - NSObject - 0x7fff8deb30f0
NSObject - 0x7fff8deb30f0 - NSObject - 0x7fff8deb3118
- - (null)

NSObject的父类打印的结果是nil。SWTeacher 的元类的父类是 SWPerson 的元类(SWPerson 的元类的地址和SWPerson 类的地址不一样)。SWPerson 的元类的父类是 NSObject 的元类,NSObject 的元类的父类是NSObject(和NSObject类的地址一样)

  • SWTeacher 继承 SWPersonSWPerson 继承 NSObjectNSObject的父类是nil
  • SWTeacher 元类继承 SWPerson 元类,SWPerson 继承根元类根元类继承NSObject

类结构分析

oc 对象的本质与isa 中发现isaClass类型的,Classobjc_class*objc_class是一个结构体,所有的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

    //下面是一些方法省略
};

objc_class继承objc_objectobjc_object里面只有一个成员变量isaisa具体是作用已经探究过。下面的三个成员变量具体的作用还未知,怎么去探究它呢?类的地址是知道的,那么就根据上面探究过的首地址+偏移值来获取里面的成员变量的地址,然后获取值。但是偏移值需要知道当前变量之前的所有成员变量的大小

  • isa 结构体指针占8字节
  • Class superclass 也是结构体指针占8字节
  • cache_t cache 是结构体类型的大小,由结构体内成员变量决定, 16字节
  • class_data_bits_t bits 知道前面三个成员变量大小,就可以得到bits的地址

cache_t内存大小

typedef unsigned long           uintptr_t;

#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;   // 4
#if __LP64__
            uint16_t                   _flags;       // 2
#endif
            uint16_t                   _occupied;    // 2
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache; //8
    };
    
    //下面是一些方法省略
};

cache_t 是结构体类型,有两个成员变量_bucketsAndMaybeMask和一个联合体

  • _bucketsAndMaybeMask是 uintptr_t无符长整型占8字节
  • 联合体里面有两个成员变量结构体和 _originalPreoptCache,联合体的内存大小由成员变量中的最大变量类型决定
  • _originalPreoptCache 是结构体指针占8字节
  • 结构体中有_maybeMask_flags_occupied_maybeMask是uint32_t占 4字节,_flags_occupied是uint16_t 各占2字节,结构体大小是8字节
  • cache_t的内存大小是 8+8 或者是8+4+2+2都是16字节

小结:通过获取class_rw_t* 类型的data(),将会拿到这个类的methodspropertiesprotocolsdeepCopyro等等信息

class_rw_t

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

//省略了部分代码
    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;


    class_rw_ext_t *deepCopy(const class_ro_t *ro) {
        return extAlloc(ro, true);
    }

    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
        }
    }

    const property_array_t properties() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
        }
    }

    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }
};

类的属性

成员变量获取流程:NSObject.class -> class_data_bits_t -> class_rw_t -> property_array_t -> property_list_t -> property_t

类的实例方法

实例方法获取流程:NSObject.class -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t -> big

成员变量

ivars获取流程源码:NSObject.class -> class_data_bits_t -> class_rw_t -> class_ro_t -> ivar_list_t -> ivar_t

变量的底层实现是ivar_t。存储在class_ro_t中的变量列表里 系统会给属性自动生成一个带_属性名变量,存储在class_ro_t中的变量列表里

类方法

类方法获取流程:NSObject.class -> metaClass -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t -> big

结论:类方法存储在元类中的方法列表里

源码

查看的源码是objc4-818.2.tar.gz