IOS开发知识点

iOS 类的结构分析

2020-09-15  本文已影响0人  ugpass

1. 类的初探

isa结构解析中,自定义LSPerson 类继承自NSObject,重写成C++代码如下

struct LSPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
};

struct NSObject_IMPL结构体定义如下

struct NSObject_IMPL {
    Class isa;
};

typedef struct objc_class *Class;
Class为指向 结构体struct objc_class的指针,所以继承自NSObject的对象中都有isa成员变量。那么NSObject的isa来自何处呢?

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

    class_rw_t *data() const {
        return bits.data();
    }

    ...省略的函数
}

由此可以知道struct objc_class继承自struct objc_object

struct objc_object源码如下

struct objc_object {
private:
    isa_t isa;

public:
    ...省略的函数
}

总结:在OC层,对象的isa指向对象的类,对象的类继承自NSObjectNSObject是以结构体struct objc_class为模版创建的;在C++层,struct objc_class继承自struct objc_objectstruct objc_object内部有isa成员变量。

2. 类的结构分析

由结构体struct objc_class源码可知,类的信息存储在class_data_bits_t bits;中。
为了取到class_data_bits_t bits;,需要在结构体struct objc_class进行地址偏移计算。

  1. Class ISA; //结构体指针类型 8字节
  2. Class superclass;//结构体指针类型 8字节
  3. cache_t cache;

cache_t源码

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;

    ...其他全局变量

#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;

    ...其他方法

cache_t结构体计算:explicit_atomic<uintptr_t> _maskAndBuckets; long类型 8字节; mask_t _mask_unused; int类型 4字节; uint16_t _flags; short类型 2字节;uint16_t _occupied; short类型 2字节。根据内存对齐原则cache_t大小为16字节。故为了取到class_data_bits_t bits;,需要在结构体struct objc_class对首地址进行偏移32字节

探索流程:

//获取LSPerson类首地址
(lldb) p/x LSPerson.class
(Class) $0 = 0x00000001000021c8 LSPerson

//获取类isa中对内存信息
(lldb) x/4gx 0x00000001000021c8
(lldb) x/4gx 0x00000001000021c8
0x1000021c8: 0x00000001000021a0 0x00007fff944ae118
0x1000021d8: 0x00007fff6cd18140 0x0000801000000000

//根据地址偏移0x1000021c8 -> 0x1000021d8 获取bits信息
(lldb) p (class_data_bits_t *)0x1000021d8
(class_data_bits_t *) $1 = 0x00000001000021e8

//根据struct objc_class中data()函数获取class_rw_t
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001010224b0

(lldb) p *($2)
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = 4294975744
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}

(lldb) p $4.methods()      //打印出对象方法列表
(lldb) p $4.properties()    //打印出属性列表
(lldb) p $4.protocols()     //打印出协议列表

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;

private: 
    ...省去其他函数

public: 
    ...省去其他函数
    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>()->ro;
        }
        return v.get<const class_ro_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 *>()->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>()->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 *>()->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>()->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 *>()->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
        }
    }
};

根据已获取的class_rw_t,调用结构体内部函数methods()properties()protocols()即可获取类的方法列表,属性列表以及协议列表。

上一篇下一篇

猜你喜欢

热点阅读