iOS进阶笔记(一) 对象本质相关源码解读
一、Class结构本质
1、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
}
struct objc_object {
// ISA() assumes this is NOT a tagged pointer object
Class ISA(bool authenticated = false);
// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
Class rawISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
}
从runtime源码中,可以看出类的本质主要由isa(指向meta-class)、superclass(指向父类指针)、cache(方法缓存)及bits(bits & FAST_DATA_MASK可以找到class_rw_t结构体中获取该类的方法列表、属性列表、协议列表)组成。
2、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;
}
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;方法列表(二维数组,包括分类方法列表method_list_t、及自身类的方法列表(通过ro获取))
property_array_t properties;// 属性列表(二维数组)
protocol_array_t protocols; // 协议列表(二维数组)
char *demangledName;
uint32_t version;
};
3、struct 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;
}
注:isa和superclass相关知识请查阅iOS 进阶笔记(三)一些关键字
二、class_getInstanceSize(Class cls)执行过程
- 通过instance的isa找到对应的Class对象
- 再到该类对象的
struct objc_class
结构中调用alignedInstanceSize()
函数
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
alignedInstanceSize(cls)
函数内部执行调word_align(unalignedInstanceSize())
函数
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
objc-os.h
文件中的word_align(x)
函数执行操作为:(x + WORD_MASK) & ~WORD_MASK
;
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
对于iOS系统WORD_MASK = 7UL
(x + WORD_MASK) & ~WORD_MASK
实质上相当于将对处于堆的对象内存按字节对齐,即先将对象内存地址左移3位,再将地址的最低3位置0。
这也就是为什么所有对象内存地址的低3位值都是0的原因。
unalignedInstanceSize()
函数内部执行了objc_class 结构体中成员为class_data_bits_t
的结构体中data()
函数
// 在struct objc_class: objc_object结构体中定义如下函数
uint32_t unalignedInstanceSize() const {
ASSERT(isRealized());
return data()->ro()->instanceSize;
}
data()函数内部执行了:(class_rw_t *)(bits & FAST_DATA_MASK);
// class_data_bits_t 结构体中函数
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);// 0x00007ffffffffff8UL
}
data()
返回类型为class_rw_t
结构体指针进一步执行了class_rw_t中的ro()
函数,ro()函数实际调用了struct class_ro_t
中instanceSize
变量值,最终得出该对象占用内存的大小。
函数调用结构图如下:
三、object_getClass (id obj) 执行过程执行过程
1、函数内部调用了 obj->getIsa()
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
2、getIsa() 内部调用了 ISA() 函数
inline Class
objc_object::getIsa()
{
// 若不是TaggedPointer类型,则返回对象的isa
if (fastpath(!isTaggedPointer())) return ISA();
extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
uintptr_t slot, ptr = (uintptr_t)this;
Class cls;
// ptr 占64位,先右移60位后 & 0x00_00_00_0F
// 即slot值为ptr的低4位的值
slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
cls = objc_tag_classes[slot];
if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
cls = objc_tag_ext_classes[slot];
}
return cls;
}
3、ISA() 内部调用了 isa.getDecodedClass(bool authenticated) 函数
inline Class
objc_object::ISA(bool authenticated)
{
ASSERT(!isTaggedPointer());
return isa.getDecodedClass(authenticated);
}
4、getDecodeedClass(bool authenticated) 内部调用了 getClass(bool)
inline Class
isa_t::getDecodedClass(bool authenticated) {
// 对于iOS设备 SUPPORT_INDEXED_ISA = 0
#if SUPPORT_INDEXED_ISA
if (nonpointer) {
return classForIndex(indexcls);
}
return (Class)cls;
#else
// 调用了 getClass
return getClass(authenticated);
#endif
}
5、getClass(bool)内部执行了 bits & ISA_MASK
inline Class
isa_t::getClass(MAYBE_UNUSED_AUTHENTICATED_PARAM bool authenticated) {
#if SUPPORT_INDEXED_ISA
return cls;
#else
uintptr_t clsbits = bits;
// 判断编译器是否支持__has_feature(ptrauth_calls)
# if __has_feature(ptrauth_calls)
# if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTH
// Most callers aren't security critical, so skip the
// authentication unless they ask for it. Message sending and
// cache filling are protected by the auth code in msgSend.
if (authenticated) {
// Mask off all bits besides the class pointer and signature.
clsbits &= ISA_MASK;// 可以看出ISA()本质是 bits & ISA_MASK
if (clsbits == 0)
return Nil;
clsbits = (uintptr_t)ptrauth_auth_data((void *)clsbits, ISA_SIGNING_KEY, ptrauth_blend_discriminator(this, ISA_SIGNING_DISCRIMINATOR));
} else {
// If not authenticating, strip using the precomputed class mask.
clsbits &= objc_debug_isa_class_mask;
}
# else
// If not authenticating, strip using the precomputed class mask.
clsbits &= objc_debug_isa_class_mask;
# endif
# else
clsbits &= ISA_MASK;// 可以看出ISA()本质是 bits & ISA_MASK
# endif
return (Class)clsbits;
#endif
}
</br>
6、当object_getClass(id obj)中obj为Instance对象时,bit & ISA_MASK 地址则为其类对象地址;
当obj为类对象时,bits & ISA_MASK 地址则为其meta-claas对象地址;
当obj为meta-class时,bits & ISA_MASK 地址则为meta-class基类对象的地址
以上
----------End------------
(限于水平,本文可能存在瑕疵甚至错误的地方。如有发现,还请留言指正,相互学习。thx! )
KEEP LOOKING, DON`T SETTLE!