OC对象的本质

2020-05-06  本文已影响0人  RainyHand

总结性文章,如有问题,请评论

image.png

从以下几个维度分析

1.对象的分类
2.内部实现
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

Class的内部实现
typedef struct objc_class *Class; 

所以ISA是一个指向结构体的指针变量

当继承发生时

struct NSobject_imp{
        Class imp;
    };
    
    struct Person {
        struct NSobject_imp imp_isa;
        int _age;
        int _num;
        int _on;
    };
通俗理解,生成的person实例对象中,包含三个ivar + NSObject的ISA指针

实例对象的结构如下:

类对象class
通过xcode查看的类对象已经过时,下边是之前的定义,最新的定义可以查看objc的源码,在下边列出几个重要的点

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

新版

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
最新版的ISA被注释掉了,不要被迷惑,只要是对象,就会有ISA,实际是objc_class继承于objc_object
而...
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

然后展开class_data_bits_t里边public的 class_rw_t, 看通过 rw可以了解,这部分是可以读写的 

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t version;
    uint16_t witness;
    const class_ro_t *ro;   //不可写部分
    method_array_t methods; //方法列表
    property_array_t properties;// 属性列表
    protocol_array_t protocols; //协议列表
    Class firstSubclass; 
    Class nextSiblingClass;
    char *demangledName;
}

这是 class_ro_t  可读不可写的部分 
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;   //实例的大小
#ifdef __LP64__
    uint32_t reserved;
#endif
  t 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;
}

通过上述几个机构提可以看出,类对象的基本结构如下

针对bits里边可变不可变的属性做个补充:

  1. method 和 protocol 这些通过运行时可以动态添加;properties可以在分类中添加(只不过值是存储在一个全局的散列表中的)
  2. ivars这个是不可变的,也就是说,不能向一个已经创建好的类中添加ivar,这是因为,在实例化实例对象的时候,内存大小是固定的。(instanceSize)
    实例结构体中包含的就是ISA指针和ivars的列表。

元类对象
元类对象是类对象的一种,内部结构和类对象一样,在创建类对象的时候,会同时创建一个元类对象出来。
只不过内部存储的值和类对象是不一样的,元类对象中存储的是类方法

总结:起始根据这三种对象,体现了程序的分层设计的原则,最上层包含的是成员变量、下一层包含各种方法和缓存、再进一层就是只有类才能调用的类方法。

3.占用空间的大小

我们创建一个实例对象,占用的内存空间的大小是怎么样的,找到最上边实例的结构体样式。

4.方法调用

直接拿网上的图


查找流程.png

先看ISA 和 superclass这两个指针的指向
ISA - > 父类
父类ISA - >元类
元类ISA - > Root Meta Class

Subclass 的superclass指针 - > Superclass
Superclass 的 superclass指针 - > NSObject

对象方法:根据ISA指针找到自己的父类-- >查找父类cache缓存 -->查找父类methodlist --> 根据superclass指针 -- > Superclass的cache --- > Superclass的methodlist ---> NSObject...

类方法:根据ISA指针找到Metaclass的cache -- > Metaclass 的methodlist -- > 根据superclass指针周到 Superclass Meta的cache ....

其他补充

1.关于ISA指针中存储的地址不是类对象首地址的原因
由于在新版ISA指针做了优化,8个字节 64位的存储空间,不仅存储指向类对象的地址,还保存了一些标志位信息,包括是否有关联对象、是否有弱引用、引用计数存储等。所以要取出这个地址还是要 & 上下边的地址。


ISA_MASK2020-02-29上午8.31.58.png

2.类方法在寻找的时候,找到NSObject的元类中,如果还是没有,还会去NSObject类中寻找是否有相同符号的对象方法。
3.获取元类的方式object_getClass([NSObject class]);传递一个类对象进去,就可以获取。

上一篇下一篇

猜你喜欢

热点阅读