从源码的角度分析类的属性、方法、实例变量、类方法

360 阅读2分钟

类的结构

从内存加载类的变化

从内存中加载流程如图所示:

image.png

clean MemoryDirty Memory

  • clean Memory是加载后不会发生更改的内存。clean Memory可以进行移除,从而节省更多空间。因为如果需要clean Memory数据,可以从内存中去加载。
  • Dirty Memory是运行时会发生更改后的内存。类一经使用就变成了Dirty Memory,因为运行时会向它写入新数据。例如创建方法缓存并从类中指向它Dirty Memory要比clean Memory昂贵的多,只要进程在运行,就一直存在

从源码看类

objc4-818.2的源码中去查看

  • Class继承objc_class,然后我们在源码中去搜索,发现有两个:
struct objc_class  // OBJC2_UNAVAILABLE

// 和
struct objc_class : objc_object

由于第一个是OBJC2_UNAVAILABLE也就是objc2不可用,所以我们选择分析第二个,这里objc_class继承objc_object

image.png

    • bits的注释处提到class_rw_t,这个是什么呢,objc_class中对它有一个存取:
class_rw_t *data() const {
    return bits.data();
}
void setData(class_rw_t *newData) {
    bits.setData(newData);
}

它是从bits.data()里获取的,去看看class_rw_t

const class_ro_t *ro()
void set_ro(const class_ro_t *ro)
const method_array_t methods()
const property_array_t properties()
const protocol_array_t protocols()
// 继承自list_array_tt
class method_array_t : public list_array_tt
class property_array_t : public list_array_tt
class protocol_array_t : public list_array_tt

获取类的属性

//
class list_array_tt {
struct array_t {
};
protected:
// iterator是 迭代器方法
    class iterator {
        const Ptr<List> *lists;
        const Ptr<List> *listsEnd;
        typename List::iterator m, mEnd;
     public:
        iterator(const Ptr<List> *begin, const Ptr<List> *end)
            : lists(begin), listsEnd(end)
        {
            if (begin != end) { // 从begin和end的位置都放到里面,意味着具备遍历能力
                m = (*begin)->begin();
                mEnd = (*begin)->end();
            }
        }
        // 取出元素
        const Element& operator * () const {
            return *m;
        }
        Element& operator * () {
            return *m;
        }
        ...
    }
...
}
// iterator 迭代器的ptr 是一个属性property_list_t *conststruct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};

// property_list_t 继承 entsize_list_tt
struct entsize_list_tt {
...
    Element& get(uint32_t i) const { 
        ASSERT(i < count);
        return getOrEnd(i);
    }
...
}


struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};

// property_list_t 继承 entsize_list_tt
struct entsize_list_tt {
...
    Element& get(uint32_t i) const { 
        ASSERT(i < count);
        return getOrEnd(i);
    }
...
}
可以使用get来取出内容get(0)
struct property_t {
    const char *name;
    const char *attributes;
};

获取类的方法

  • 获取bits地址
  • 获取data数据
  • 获取 class_rw_t 中的 方法数组
  • 获取方法列表的指针数组
  • 获取ptr
  • 获取内存信息get(0)[由于和属性不一样]
  • get(0).big
struct method_t {
   struct big {
    SEL name;
    const char *types;
    MethodListIMP imp;
};
    ...
}

获取类实例变量(ivar)

image.png

然后class_ro_t里面有个ivar_list_t是实例变量列表

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__ // 表示指针长度为64位,即地址长度以64位长度来表示
    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;
    ...
}

然后就能拿到实例变量

(ivar_t) $7 = { 
    offset = 0x00000001000082a8 
    name = 0x0000000100003f3f "subject" 
    type = 0x0000000100003f89 "@\"NSString\"" 
    alignment_raw = 3 
    size = 8 
    }

获取类方法

类方法存储在元类里,元类的结构和类一样,先拿到元类,然后根据前面的方法拿method

总结

  1. 获取属性:我们拿到当前bits.data,它的类型是class_rw_t,然后去找class_rw_tproperties数据,最后拿到property_list_t数组,然后根据get()方法,传入属性位置,进而拿到属性。
  2. 获取实例方法:我们拿到class_rw_t数据后,然后去拿里面的methods(),然后一步步往里面拿,拿到method_t的数据类型,再调用里面的big方法就拿到了实例方法
  3. 获取实例变量:我们拿到class_rw_t后,然后拿到里面的ro方法,在拿到ivars信息,然后一步步拿到entsize_list_tt数组,再根据get()传入位置,就拿到了实例变量
  4. 获取类方法:我们先拿到元类,然后根据总结2的步骤,最后就拿到了类方法