这一章我们来探索一下alloc
源码核心中的
obj->initInstanceIsa(cls, hasCxxDtor)
:将类与isa指针关联
step1:来到objc-object.h
中的initInstanceIsa
的源码中
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
step2:来到objc-object.h
中的initIsa
的源码中
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
if (!nonpointer) {
isa = isa_t((uintptr_t)cls); //isa 初始化
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
isa_t newisa(0);//isa 初始化
#if SUPPORT_INDEXED_ISA // 通过cls初始化
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 // 通过bits初始化
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
//shiftcls 从第三位开始
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// 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
是isa_t
类型的
来到objc-private.h
中isa_t
的源码
//联合体
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
//提供了cls 和 bits ,两者是互斥关系
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
联合体
联合体也是由不同的数据类型组成,但其变量是互斥的,所有的成员共占一段内存。而且共用体采用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会将原来成员的值覆盖掉
- 优点:所有成员共用一段内存,使内存的使用更为精细灵活,同时也节省了内存空间
- 缺点:包容性弱
与结构体的区别
结构体的各个成员会占用不同的内存,互相之间没有影响
共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员
结构体内存 >= 所有成员占用的内存总和(成员之间可能会有缝隙)
共用体占用的内存等于最大的成员占用的内存
isa_t
是一个union
联合体
-
Class cls
代表isa
关联的类的类型, -
uintptr_t bits
是一段保存着isa
指针内存优化,这里的内存优化是指在isa
指针中通过char + 位域
(即二进制中每一位均可表示不同的信息)的原理实现。通常来说,isa
指针占用的内存大小是8字节,即64位,已经足够存储很多的信息了,这样可以极大的节省内存,以提高性能 -
ISA_BITFIELD
,bits
的位域,是一个宏定,全局搜索ISA_BITFIELD
在isa.h
中可以找到它的定义
image.png
__arm64__
iOS(真机)
__ax86_64__
macOS(模拟器)
uintptr_t nonpointer
:是否对isa
指针开启指针优化
uintptr_t has_assoc
:关联对象标志位
uintptr_t has_cxx_dtor
:表示该对象是否有C++/OC的析构器
uintptr_t shiftcls
:存储类的指针的值(类的地址),真机占33位/模拟器占44位
uintptr_t magic
:调试器判断对象是真对象还是未初始化空间
uintptr_t weakly_referenced
:对象是否被指向或者曾经指向一个ARC的若变量
uintptr_t deallocating
:标志对象是否正在释放内存
uintptr_t has_sidetable_rc
:当对象引用计数大于10时,则需要借用该变量存储进位。
uintptr_t extra_rc
:表示该对象的引用计数值,实际上是引用计数值减1,例如,如果对象的引用计数为10,那么extra_rc
为9,如果大于10,就需要用到上面的has_sidetable_rc
真机占19位/模拟器占8位
initIsa
源码分析
运行objc4-781
源码,来到initIsa
方法,添加断点,
step1 在初始化后打印newisa
step2bits
赋值ISA_MAGIC_VALUE
ISA_MAGIC_VALUE
宏源码
define ISA_MAGIC_VALUE 0x001d800000000001ULL
打印newisa
bits
赋值后,magic = 59
,59的二进制 0x001d800000000001ULL
step3 shiftcls
赋值(uintptr_t)cls >> 3
在
shiftcls
赋值后,cls
也有值了
isa
指针的应用
平时开发过程中,我们是通过object_getClass
获取类名称
看下这个方法的源码
来到objc-class.mm
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
来到objc-object.h
中的getIsa
inline Class
objc_object::getIsa()
{
if (fastpath(!isTaggedPointer())) return ISA();
extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
uintptr_t slot, ptr = (uintptr_t)this;
Class cls;
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;
}
来到objc-object.h
中的ISA()
inline Class
objc_object::ISA()
{
ASSERT(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
原来重写了isa
的get
方法,通过isa.bits & ISA_MASK
获取bits
里面存储的类信息,然后强转成Class
类型
网友评论