了解OC对象本质

前言

  • 本文只是作为自己对学习的一个总结,写了很久了一直放在印象笔记中,拿出来放在上,一方面可以温习一下,另一方面和大家共享学习经验,如果有错误的地方欢迎各位大神提出
  • 文笔不行,可能会写的有点乱,请见谅!

我们用到的应该只有三种对象

  • 实例对象 (instance)
  • 类对象 (class)
  • 元类对象(meta)

类对象(class)

我们可以通过源码-723对对象进行分析
类对象在objc-runtime-old.h的定义

struct objc_class : objc_object {
    Class superclass;
    const char *name;
    uint32_t version;
    uint32_t info;
    uint32_t instance_size;
    struct old_ivar_list *ivars;
    struct old_method_list **methodLists;
    Cache cache;
    struct old_protocol_list *protocols;
}

在objc-runtime-new.h下就发生了变化,我们着重看新的

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
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();
    }
}

内部结构:
isa : 指向元类的指针(因为objc_class继承自objc_object,所以内部也会有isa指针)当一个类对象调用某个方法的时候,会根据类对象的isa指针找到对应的元类对象,在元类对象的bits里面找到类对象的方法的实现;
superclass:父类类对象
cache : 方法缓存
bits : 类的详细信息(方法列表,协议列表,属性列表)

    NSObject *objc = [[NSObject alloc]init];
    Class class1 = [objc class];
    Class class2 = [NSObject class];
    Class class3 = objc_getClass("NSObject");
    NSLog(@"objc = %p",objc);
    NSLog(@"class1 =%p",class1);
    NSLog(@"class2 =%p",class2);
    NSLog(@"class3 =%p",class3);
image.png

一个类有且只有一个类对象:用来存储对象方法列表,协议列表,superclass,属性列表,成员变量信息

实例对象(instance)

创建一个Person类,继承自NSObject

@interface Person : NSObject
{
    int _age;
    int _num;
}
@end
@implementation Person

@end

用clang编译成C++代码,如下:

struct NSObject_IMPL {
    Class isa;
};
struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
};

从上可以看出一个实例对象也是一个结构体包含了:

  • isa : 指向了类对象
  • _age : 成员变量
    记得以前和同事讨论过一个问题:类对象的内部有个properties,是不是所有的属性都存储在这呢?如果是,那一个类可以创建多个对象,那每个对象的属性存储的值是不一样的,类对象内部是怎么进行存储的呢?我们用下面的例子实验一下
struct Person_IMPL{
    Class isa;
    int _age;
    int _num;
};
        Person *person = [[Person alloc]init];
        person->_age = 20;
        person->_num = 30;
        Person *person2 = [[Person alloc]init];
        struct Person_IMPL *personImpl = (__bridge struct Person_IMPL *)(person);
        struct Person_IMPL *personImpl2 = (__bridge struct Person_IMPL *)(person2);
        NSLog(@"age = %d num = %d",personImpl->_age,personImpl->_num);
        NSLog(@"age = %d num = %d",personImpl2->_age,personImpl2->_num);
image.png

从上面的例子中,可以看出:对象成员变量的值是存储在实例对象的结构体中的,但是我还是有疑惑,既然对象的实例变量的值是存储在实例对象的结构体中的,类对象结构体中存储的properties ,ivars是用来干描述信息的,只存储一份就够了;

总结一下: 实例对象中只存储了成员变量,不会存储方法,方法是存储在类对象和元类对象中的,以后的文章中会写到方法调用流程
一个对象占用多少内存呢?

        Person *person = [[Person alloc]init];
        NSLog(@"%zu",class_getInstanceSize([NSObject class]));
        NSLog(@"%zu",class_getInstanceSize([person class]));
        NSLog(@"%zu",malloc_size((__bridge const void *)(person)));

打印结果为: 8 , 16 ,16(64位架构下)
NSObject对象占用8个字节,因为里面只有一个isa指针
person占用了16字节,因为person继承自NSObject,所以内部也会有isa指针,两个int成员共占用8字节,也就是16字节

元类对象(meta)

用来存储类方法的对象;怎么获取元类对象呢?

    Class class1 = object_getClass([objc class]);
    Class class2 = object_getClass([NSObject class]);
    BOOL isMeta = class_isMetaClass(class1);
    NSLog(@"class1 =%p class2 =%p ",class1,class2);
    NSLog(@"class1 is meta = %d",isMeta);

image.png

class_isMetaClass(Class _Nullable cls):传入类对象返回元类对象,传入实例对象返回类对象
元类对象也是有且只有一个的,元类对象内部结构和类对象是一样的,因为他们都是Class类型的,只不过里面存储的内容不一样
了解OC对象本质_第1张图片
对象关系图

OC中三种对象类型就上面这些了,后面的文章中还会进行详细点的讲解

你可能感兴趣的:(了解OC对象本质)