OC对象的本质

256 阅读4分钟

原来对象内部是一个Class类型的isa指针,Class的结构如下:

Class结构体
后面提示OBJC2_UNAVAILABLE已经过期了,从苹果源码网站可以下载最新代码。

看到objc_class已经变得很C++了

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);
    }
    ...
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    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;
    ...
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;
    }
};

他们的关系如图:

isa指针指向图例

一个NSObject对象占用多少内存?

NSObjcet实际上是只有一个名为isa的指针的结构体,因此占用一个指针变量所占用的内存空间大小,如果64bit占用8个字节,如果32bit占用4个字节。

什么是内存对齐,原则是什么?

编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能是该基本数据类型的整倍的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为对齐模数。 为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节

内存对齐的两个原则:

  • 原则 1. 前面的地址必须是后面的地址正数倍,不是就补齐。
  • 原则 2. 整个Struct的地址必须是最大字节的整数倍。

OC的类信息存放在哪里?

OC对象主要可以分为三种: instance对象(实例对象) class对象(类对象) meta-class对象(元类对象).

获取方式分别如下:

NSObject *object = [[NSObject alloc] init];//instance对象
Class objectClass = [NSObject class];//class对
Class objectMetaClass = object_getClass([NSObject class]);//meta-class对
        
NSLog(@"%p %p %p", object, objectClass, objectMetaClass);

instance对象在内存中存储的信息包括: isa指针 其他成员变量

每一个类在内存中有且只有一个class对象。可以通过打印内存地址证明 class对象在内存中存储的信息主要包括: isa指针 superclass指针 类的属性信息(@property),类的成员变量信息(ivar) 类的对象方法信息(instance method),类的协议信息(protocol)

类方法放在那里? 元类对象 meta-class

//runtime中传入类对象此时得到的就是元类对象
Class objectMetaClass = object_getClass([NSObject class]);
// 而调用类对象的class方法时得到还是类对象,无论调用多少次都是类对象
Class cls = [[NSObject class] class];
Class objectClass3 = [NSObject class];
class_isMetaClass(objectMetaClass) // 判断该对象是否为元类对象
NSLog(@"%p %p %p", objectMetaClass, objectClass3, cls); // 后面两个地址相同,说明多次调用class得到的还是类对象

每个类在内存中有且只有一个meta-class对象。 meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括:

  1. isa指针
  2. superclass指针
  3. 类的类方法的信息(class method)

meta-class对象和class对象的内存结构是一样的,所以meta-class中也有类的属性信息,类的对象方法信息等成员变量,但是其中的值可能是空的。

衍生问题:在实例对象中根本没有看到对象方法,那么实例对象的方法的代码放在什么地方呢?那么类的方法的信息,协议的信息,属性的信息都存放在什么地方呢?

成员变量的值时存储在实例对象中的,因为只有当我们创建实例对象的时候才为成员变赋值。但是成员变量叫什么名字,是什么类型,只需要有一份就可以了。所以存储在class对象中。

对象的isa指针指向哪里?

instance对象的isa指针指向class对象,class对象的isa指针指向meta-class对象,meta-class对象的isa指针指向基类的meta-class对象,基类自己的isa指针也指向自己。

Link