[iOS]-OC对象底层探索

类与对象

类和对象的本质

对象部分

OC的类和对象本质上都是结构体,在编译时都会以结构体的形式被编译到二进制中。
我们直接来看runtime源码:

struct objc_object {
private:
    isa_t isa;

public:
....省略方法部分(该部分非常多)

除去方法部分,我们可以发现成员变量只有一个isa_t类型的isa
而isa_t这个 类型只有一个成员变量,存储了对象所属类的信息[Class cls]

**总结:**对象的本质除了其本身含有的方法列表,它有一个isa指针用来存储所属类的信息。

类部分

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // 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_object,换句话说,类的本质也是个对象,其中isa指针式其继承自objc_object的,所以代码里才注释了// Class ISA;
isa是结构体,其中会存储类的地址,其并不是指针
在这里插入图片描述
这里我们先来看一下对象、类、元类的示例图:
在这里插入图片描述
元类的定义:
OC对象在发送消息时,运行时库会追寻着对象的isa指针得到对象所属的类。这个类包含了能应用于这个类的所有实例方法以及指向父类的指针,以便可以找到父类的实例方法。运行时库检查这个类和其父类的方法列表,找到与消息对应的方法。 编译器会将消息转换为消息函数objc_msgSend进行调用

我们有时也会有对类发送消息的情况:

NSString *testString = [NSString stringWithFormat:@"%@",@"123"];

从此处我们可以知道,OC的类其实也是一个对象,一个对象就要有一个它属于的类,意味着类也要有一个isa指针,指向其所属的类。那么类的类是什么呢?就是我们所说的元类(MetaClass),所以,元类就是类对象的所属类

所以从消息机制的层面来讲:

  • 当你给对象发消息时,消息会寻找这个对象的类的方法列表
  • 当你给类发消息时,消息是在寻找这个类的元类的方法列表

OC的类信息存放在哪里?
答:对象方法、属性、成员变量、协议信息,存放在class对象中
类方法存放在meta-class对象中(元类对象和class内存结构是一样的 但是用途不一样 主要有类方法的类信息 其他为空的)
成员变量的具体值存放在instance对象中

isa详解

isa_t结构体

这里我们来详解一下isa指针,即isa_t结构体

每个OC对象都含有一个isa指针,__arm64__之前,isa仅仅是一个指针,保存着对象或类对象内存地址,在__arm64__架构之后,apple对isa进行了优化,变成了一个共用体(union)结构,同时使用位域来存储更多的信息。

先看一下isa_t的代码定义:

union isa_t 
{
    Class cls;
    uintptr_t bits;
    //bits的结构体
    struct {
         uintptr_t nonpointer        : 1;//->表示使用优化的isa指针
         uintptr_t has_assoc         : 1;//->是否包含关联对象
         uintptr_t has_cxx_dtor      : 1;//->是否设置了析构函数,如果没有,释放对象更快
         uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 ->类的指针
         uintptr_t magic             : 6;//->固定值,用于判断是否完成初始化
         uintptr_t weakly_referenced : 1;//->对象是否被弱引用
         uintptr_t deallocating      : 1;//->对象是否正在销毁
         uintptr_t has_sidetable_rc  : 1;//1->在extra_rc存储引用计数将要溢出的时候,借助Sidetable(散列表)存储引用计数,has_sidetable_rc设置成1
        uintptr_t extra_rc          : 19;  //->存储引用计数,实际的引用计数减一,存储的是其对象以外的引用计数
    };
};

在这里插入图片描述
在这里插入图片描述

  • nonpointer:表示是否对 isa 指针开启指针优化 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等。
  • has_assoc:关联对象标志位,0没有,1存在。
  • has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象。
  • shiftcls:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
  • magic:用于调试器判断当前对象是真的对象还是没有初始化的空间。
  • weakly_referenced:对象是否被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放。
  • deallocating:标志对象是否正在释放内存
  • has_sidetable_rc:当对象引用技术大于 10 时,则需要借用该变量存储进位。
  • extra_rc:当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的 has_sidetable_rc。

isa的初始化过程

看过了isa_t的底层结构,现在我们来看一下isa 的初始化过程,来探索isa_t 的底层实现,初始化过程发生在alloc方法的底层流程中的这一步:

    /// 将类和指针做绑定
    obj->initInstanceIsa(cls, hasCxxDtor);

此过程包含了 isa 的初始过程,要探究 isa 的底层实现,我们就从 isa 的创建开始:

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
	//下方函数调用就是isa的初始过程
    initIsa(cls, true, hasCxxDtor);
}

initIsa() 内容如下:

inline void 
objc_object::initIsa(Class cls)
{
    initIsa(cls, false, false);
}

inline void 
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ 
    ASSERT(!isTaggedPointer()); 
    
    isa_t newisa(0);

    if (!nonpointer) {
        newisa.setClass(cls, this);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());


#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
#   if ISA_HAS_CXX_DTOR_BIT
        newisa.has_cxx_dtor = hasCxxDtor;
#   endif
        newisa.setClass(cls, this);
#endif
        newisa.extra_rc = 1;
    }

    // This write must be performed in a single store in some cases
    // (for example when realizing a class because other threads
    // may simultaneously try to use the class).
    // fixme use atomics here to guarantee single-store and to
    // guarantee memory order w.r.t. the class index table
    // ...but not too atomic because we don't want to hurt instantiation
    isa = newisa;
}

我们可以看到方法内创建了一个 isa_t 类型的 newisa 实例, 做了 赋值操作后,返回了 newisa。 那么,接着我们就来详细的看一下这个 isa_t 的底层实现。
下面我们来看一下runtime中的源码定义的isa_t共用体:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    uintptr_t bits;

private:
    // Accessing the class requires custom ptrauth operations, so
    // force clients to go through setClass/getClass by making this
    // private.
    Class cls;

public:
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };

    bool isDeallocating() {
        return extra_rc == 0 && has_sidetable_rc == 0;
    }
    void setDeallocating() {
        extra_rc = 0;
        has_sidetable_rc = 0;
    }
#endif

    void setClass(Class cls, objc_object *obj);
    Class getClass(bool authenticated);
    Class getDecodedClass(bool authenticated);
};

isa_t 是一个联合体, 有两个成员变量一个是 bits, 还有一个 是 cls。我们知道 联合体 中各变量是互斥的, 它的优点是内存使用更为精细灵活。 所以,也就是说, isa_t 有两种初始化方式:

  • bits 被赋值, cls 没有值或者值被覆盖;
  • cls 被赋值, bits 没有值或者值被覆盖。

ISA_BITFIELD

isa_t 中还有一个成员变量 是 结构体 ISA_BITFIELD, 这个宏定义对应 arm64x86_64 即 iOS 和 MacOS 两个端的实现。 ISA_BITFIELD 通过位域存储信息,具体存储信息如下:

# if __arm64__
// ARM64 simulators have a larger address space, so use the ARM64e
// scheme even when simulators build for ARM64-not-e.
#   if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
#     define ISA_MASK        0x007ffffffffffff8ULL
#     define ISA_MAGIC_MASK  0x0000000000000001ULL
#     define ISA_MAGIC_VALUE 0x0000000000000001ULL
#     define ISA_HAS_CXX_DTOR_BIT 0
#     define ISA_BITFIELD                                                      \
        uintptr_t nonpointer        : 1;                                       \
        uintptr_t has_assoc         : 1;                                       \
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t shiftcls_and_sig  : 52;                                      \
        uintptr_t has_sidetable_rc  : 1;                                       \
        uintptr_t extra_rc          : 8
#     define RC_ONE   (1ULL<<56)
#     define RC_HALF  (1ULL<<7)
#   else
#     define ISA_MASK        0x0000000ffffffff8ULL
#     define ISA_MAGIC_MASK  0x000003f000000001ULL
#     define ISA_MAGIC_VALUE 0x000001a000000001ULL
#     define ISA_HAS_CXX_DTOR_BIT 1
#     define ISA_BITFIELD                                                      \
        uintptr_t nonpointer        : 1;                                       \
        uintptr_t has_assoc         : 1;                                       \
        uintptr_t has_cxx_dtor      : 1;                                       \
        uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
        uintptr_t magic             : 6;                                       \
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t unused            : 1;                                       \
        uintptr_t has_sidetable_rc  : 1;                                       \
        uintptr_t extra_rc          : 19
#     define RC_ONE   (1ULL<<45)
#     define RC_HALF  (1ULL<<18)
#   endif

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_HAS_CXX_DTOR_BIT 1
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t unused            : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

// SUPPORT_PACKED_ISA
#endif

为来更直观的理解上面 位域 ISA_BITFIELD存储的信息, 我们画个图来解析一下以上这段很长的代码。

arm64架构下的isa:
在这里插入图片描述
x86_64架构下的isa:
在这里插入图片描述
各变量代表的意思上方已经做过讲解了。

另外,我们发现源码中经常会用到assert()函数,该函数的作用如下:
其作用是如果它的条件返回错误,则终止程序执行。
assert的作用是先计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。

附加:初始化isa_t的代码为:

newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE

这个里面说的很明白 news.bits初始化时候只设置了nonpointer,magic两个部分,其余部分都没有进行设置,故值都为0

一个扩展:

# if __arm64__
#     define ISA_MASK        0x007ffffffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL

以上四行代码,是取自结构体 ISA_BITFIELD 中的内容。
这里的 ISA_MASK 是一个掩码。 我们知道 isa 的 8字节 - 64位存储空间中 在 arm64 也就是手机端 shiftcls 存储类指针的值 占用的是第4位开始33位长度的一段内存空间; 在 x86_64 也就是电脑端, shiftcls 存储类指针的值 占用的是第4位开始44位长度的一段内存空间。我们调试台打印出来一个对象的内存地址后,取第一位存储的isa地址&上对应的掩码后,就是对象的类指针的值。
下面,验证一下:
请添加图片描述
接着我们在类对象的地址上继续进行操作得到了其元类对象的地址:
请添加图片描述

class方法

首先Class这里需要注意几点

  1. Class ObjectClass = [[NSObject class] class]; 返回的还是class对象,并不是meta-class对象。- (Class)class, +(Class)class返回的就是类对象
	//instance实例对象
    NSObject *object1 = [[NSObject alloc] init];
    NSObject *object2 = [[NSObject alloc] init];

    //class对象,类对象
    //objectClass1~5都是NSObject的类对象
    Class objectClass1 = [object1 class];
    Class objectClass2 = [object2 class];
    Class objectClass3 = [NSObject class];
    Class objectClass4 = object_getClass(object1);
    Class objectClass5 = object_getClass(object2);
        
    //元类对象(将类对象当作参数传入进去)
    Class objectMetaClass = object_getClass([NSObject class]);
    Class objectMetaClass2 = [[NSObject class] class];
        
    //判断是不是元类对象

    NSLog(@"instance - %p %p", object1, object2);
    NSLog(@"class - %p %p %p %p %p %d", objectClass1,objectClass2, objectClass3, objectClass4, objectClass5, class_isMetaClass(objectClass3 ));
    NSLog(@"mateClass - %p %p %d",objectMetaClass, objectMetaClass2, class_isMetaClass(objectMetaClass));

输出情况:
instance - 0x100511920 0x10050e840
class - 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0x7fff91da2118 0
mateClass - 0x7fff91da20f0 0x7fff91da2118 1

无论多少次class方法得到的都还是类函数。
  1. 元类对象和class内存结构是一样的 但是用途不一样 主要有类方法的类信息 其他为空的
  2. 类对象在内存中有且仅有一个对象 主要包括 isa指针 super Class指针 类的属性信息 类的对象方法信息 类的协议信息 类的成员变量信息

我们再来看一下objc_class的源码部分

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA;
    Class superclass;
    cache_t cache;              // 方法缓存 formerly cache pointer and vtable
    class_data_bits_t bits;    // 用于获取具体的类信息 class_rw_t * plus custom rr/alloc flagsflags
    ...

bits里面存储了类的方法列表等等的信息

这里的bits是class_data_bits_t类型的,上面objc_object的isa_t类型数据中也有一个uintptr_t类型的bits,但是这是两种结构。

我们先看bits的数据结构 class_data_bits_t

struct class_data_bits_t {
    friend objc_class;
    // Values are the FAST_ flags above.
    uintptr_t bits;
    public:
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    // Get the class's ro data, even in the presence of concurrent realization.
    // fixme this isn't really safe without a compiler barrier at least
    // and probably a memory barrier when realizeClass changes the data field
    const class_ro_t *safe_ro() const {
        class_rw_t *maybe_rw = data();
        if (maybe_rw->flags & RW_REALIZED) {
            // maybe_rw is rw
            return maybe_rw->ro();
        } else {
            // maybe_rw is actually ro
            return (class_ro_t *)maybe_rw;
        }
    }
}

其实最最重要的是其中的2个方法。datasafe_ro,两个方法分别返回class_rw_tclass_ro_t

这里ro_t的获取也是通过data方法获取的,所有可以理解为rw_t也在ro_t之中。

class_rw_t和class_ro_t

bits里面有获取class_rw_tclass_ro_t的两个方法

class_rw_t

继续来看class_rw_t的数据结构

	上方代码省略...
    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};
        }
    }
};

rw_t中可以 看到有这3个方法,通过这三个方法分别能获取到类的方法、属性、协议。

class_ro_t

再看class_ro_t的数据结构。

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
    _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];

    _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            return _swiftMetadataInitializer_NEVER_USE[0];
        } else {
            return nil;
        }
    }

    const char *getName() const {
        return name.load(std::memory_order_acquire);
    }
    //methodListPointerDiscriminator是 方法列表指针鉴别器
    static const uint16_t methodListPointerDiscriminator = 0xC310;
#if 0 // FIXME: enable this when we get a non-empty definition of __ptrauth_objc_method_list_pointer from ptrauth.h.
        static_assert(std::is_same<
                      void * __ptrauth_objc_method_list_pointer *,
                      void * __ptrauth(ptrauth_key_method_list_pointer, 1, methodListPointerDiscriminator) *>::value,
                      "Method list pointer signing discriminator must match ptrauth.h");
#endif

    method_list_t *baseMethods() const {
#if __has_feature(ptrauth_calls)
        method_list_t *ptr = ptrauth_strip((method_list_t *)baseMethodList, ptrauth_key_method_list_pointer);
        if (ptr == nullptr)
            return nullptr;

        // Don't auth if the class_ro and the method list are both in the shared cache.
        // This is secure since they'll be read-only, and this allows the shared cache
        // to cut down on the number of signed pointers it has.
        bool roInSharedCache = objc::inSharedCache((uintptr_t)this);
        bool listInSharedCache = objc::inSharedCache((uintptr_t)ptr);
        if (roInSharedCache && listInSharedCache)
            return ptr;

        // Auth all other small lists.
        if (ptr->isSmallList())
            ptr = ptrauth_auth_data((method_list_t *)baseMethodList,
                                    ptrauth_key_method_list_pointer,
                                    ptrauth_blend_discriminator(&baseMethodList,
                                                                methodListPointerDiscriminator));
        return ptr;
#else
        return (method_list_t *)baseMethodList;
#endif
    }
    下方代码省略...

可以看到有方法、属性、协议和成员变量。但方法、属性、协议的命名都是base开通的。

ro 和 rw的区别

从生成时机的角度来说,
ro编译阶段生成,rw运行的时候生成。从存储的内容角度来讲,ro中有方法、属性、协议和成员变量,而rw中并没有成员变量。rw中的方法属性协议的取值方法中,也是通过取ro或者rwe中的值来获得。ro中的方法、属性、协议都是base,也就是只有本类中的方法属性和协议。

class_rw_ext_t

class_rw_ext_t的数据结构如下:

struct class_rw_ext_t {
    DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
    class_ro_t_authed_ptr<const class_ro_t> ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char *demangledName;
    uint32_t version;
};

class_ro_t是只读的clean memory,所以可以不用的时候进行移除存放到磁盘中,从而节省很多内存,等到用的时候再从磁盘中重新加载。然而class_rw_t是可以运行时进行修改的dirty memory,所以只要进程在运行,它就必须一直存在,所以dirty memory要比clean memory昂贵的多。但由于class_rw_t中的类也只有大约10%的类在运行时真正修改了它们的方法,所以就诞生了class_rw_ext_tclass_rw_ext_t就是把class_rw_t中平时不用的部分拆掉并存起来,使class_rw_t的大小减少很多,大大减轻了class_rw_t对内存的占用。

rw_ext_t生成条件:

  1. 使用分类的类
  2. 使用Runtime API动态修改类的结构的时候
在遇到以上2种情况的时候,类的结构(属性、协议、方法)发生改变,原有的ro(Claer Memory,便宜)已经不能继续记录类的属性、协议、方法信息了,于是系统重新生成可读可写的内存结构rw_ext(Dirty Memory, 比较贵),来存放新的类结构。

然后再取方法、属性、列表的时候,在方法实现中来做区分,如果有rw_ext的类,其列表就从那个rw_ext中获得,如果没有,从ro中读取,其代码如下:

    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        //如果有rwe,其列表就从那个rw_ext中获
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
        } else {
        	//如果没有,返回ro的baseMethod
            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};
        }
    }
};

这样做的目的是为了在不增加成本的情况下,提高性能。使用分类和使用Runtime改变类结构的情况一般占10%左右,这10%的类结构苹果使用比较贵的内存记录(可读可写),而没有变过的类结果,我们用便宜的内存记录(只读)。这样既保证了语言的动态性,又不会因为动态性而增加过多的成本。

具体的class_rw_tclass_ro_t以及class_rw_ext_t内容可以参考大佬博客:iOS八股文(四)类对象的结构(下)

一个思考:
元类里面存放的内容只有类方法,那么为什么不把类方法存放在类对象中?或者说设计元类的目的是什么呢?

1.单一职责设计原理。实例对象存储成员变量的值,类对象存放,实例方法、协议、成员变量、属性,元类对象存放类方法,各司其职,互不影响。

2.复用msgSend消息发送机制。

3.类方法、实例方法是在上层的定义,在底层并不区分类方法实例方法,但在runtime这一层,需要承接上层类方法和实例方法,对接到底层方法调用。使用了msgSend,如果msgSend的时候需要再区分类对象,实例对象,会在内部增加判读逻辑,从而降低了效率,有了元类的存在,问题迎刃而解。

cache_t
struct cache_t {
	    struct bucket_t *buckets() const;
	    mask_t mask() const;
	    mask_t occupied() const;
}

这里引入了bucket_t(散列表)
cache_t 哈希表结构,哈希表内部存储的bucket_t
bucket_t中存储的是SEL和IMP的键值对(函数名及其实现)

struct bucket_t {
private:
    // IMP-first is better for arm64e ptrauth and no worse for arm64.
    // SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
    explicit_atomic<uintptr_t> _imp;
    explicit_atomic<SEL> _sel;
#else
    explicit_atomic<SEL> _sel;
    explicit_atomic<uintptr_t> _imp;
#endif

在这里插入图片描述

property

我们都只知道property会帮属性自动生成set\get方法和一个成员变量

struct property_t {
    const char *name;
    const char *attributes;
};

我们只储存了其name和属性关键字
在这里插入图片描述
一些细节的点:

在这里插入图片描述
导入#import "objc/runtime.h"之后的object_getClass就是得到其isa指针

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

superClass得到的就是父类的指针。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值