文章目录
内容会陆续补充
isa简单了解
每个OC对象都含有一个isa指针,__arm64__之前,isa仅仅是一个指针,保存着对象或类对象内存地址,在__arm64__架构之后,apple对isa进行了优化,变成了一个共用体(union)结构,同时使用位域来存储更多的信息。
- 这里说一下struct和union的区别
- 两者都可以包含多个不同类型的数据,如int、double、Class等。
- 在struct中各成员有各自的内存空间,一个struct变量的内存总长度大于等于各成员内存长度之和;而在union中,各成员共享一段内存空间,一个union变量的内存总长度等于各成员中内存最长的那个成员的内存长度。
- 对struct中的成员进行赋值,不会影响其他成员的值;对union中的成员赋值时,每次只能给一个成员赋值,同时其它成员的值也就不存在了。
isa的数据结构其实是isa_t,是一个共用体(union修饰),意味着共用内存。
结构如下
isa的bits成员变量
isa的bits成员变量类型是uintptr_t,它实质上是个unsigned long,在64位架构下bits长度为64位,也就是8字节其中每一位的存储使用了位域,即ISA_BITFIELD。
- 位域或位段
一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。
优点:
节省储存空间;
可以很方便的访问一个整数值的部分内容从而可以简化程序源代码。
缺点:
其内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,这导致了位域在本质上是不可移植的。
宏ISA_DITDIELD
关于这个宏定义在不同架构下的表示部分如下,这里只截取了部分,大概看一下就可,下面会详细介绍
在64位下,isa其实还是占8个字节内存,共64位个二进制位
- 由于共用体特性, cls , bits 以及 struct 都是 8 字节内存 , 也就是说他们在内存中是完全重叠的。
- 实际上在 runtime 中,任何对 struct 的操作和获取某些值,如 extra_rc,实际上都是通过对 bits 做位运算实现的。
- bits 和 struct 的关系可以看做 : bits 向外提供了操作 struct 的接口,而 struct 本身则说明了 bits 中各个二进制位的定义。
以获取有无关联对象来举例 :
可以直接使用 isa.has_assoc , 也就是点语法直接访问 bits 中第二个二进制位中的数据 . ( arm 64 架构中 )
关于ISA_BITFIELD中每一个字段所存储的内容,我们以arm64为例
# 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
对于extra_rc存储的是引用计数-1的理解,它存储的是对象自身以外的引用计数,但随着源码的更新,创建对象时对isa的初始化中,对extra_rc初始化为1,所以extra_rc存储的内容直接就是对象的引用计数,这里我们不必纠结到底减不减1,只需要知道它存储的是对象的引用计数。
对于在不同的架构下,存储的数据没有变,只是占据位不同
isa的cls成员变量
由源码可知,cls是Class类型,而Class其实是指向objc_class结构体的指针变量,即cls就是指向objc_class结构体的指针变量。
总结
对于上面对isa结构的分析,我们可以知道isa其实分为两种
cls或bits
也就是
- 纯指针,指向内存地址
- NON_POINTER_ISA,除了内存地址,还存有一些其他信息
参考,所以isa的结构大致内容如下
union isa_t
{
Class cls;
uintptr_t 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; //->存储引用计数
};
};
sideTables、sideTable 简单了解
sideTablesSideTables() 方法返回的是一个 StripedMap& 类型的引用:
// StripedMap<T> is a map of void* -> T, sized appropriately
// for cache-friendly lock striping.
// For example, this may be used as StripedMap<spinlock_t>
// or as StripedMap<SomeStruct> where SomeStruct stores a spin lock.
template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum {
StripeCount = 8 };
#else
enum {
StripeCount = 64 };
#endif
struct PaddedT {
T value alignas(CacheLineSize);
};
PaddedT array[StripeCount];
...
}
StripedMap 是一个模板类, 内部维护一个大小为 StripeCount 的数组, 数组的成员为结构体 PaddedT, PaddedT 结构体只有一个成员 value, value 的类型是 T.
StripedMap< SideTable >, 说明类内的结构体的成员类型为SideTable,根据源码中的注释可知StripedMap是一个void* -> T的映射。哈希值(value,即元素sideTable)可根据对象的地址(key)得到。
sideTable
struct SideTable {
spinlock_t slock;
RefcountMap refcnts; //存引用计数的哈希表表
weak_table_t weak_table; //用于存储对象弱引用的哈希表
}
RefcountMap是一个hash map,其key是obj的DisguisedPtr<objc_object>。value,则是obj对象的引用计数,同时,这个map还有个加强版功能,当引用计数为0时,会自动将对象数据清除。
//
static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;
下面在retainCount中会有从sidetables、sideTable中获取value的步骤
retainCount实现
- (NSUInteger)retainCount {
return _objc_rootRetainCount(self);
}
_objc_rootRetainCount(id obj)
{
ASSERT(obj);
return obj->rootRetainCount();
}
objc_object::rootRetainCount()
{
//标记指针 直接返回
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = __c11_atomic_load((_Atomic uintptr_t *)&isa.bits, __ATOMIC_RELAXED);
//nonpointer 为1 表示优化过的isa指针
if (bits.nonpointer) {
uintptr_t rc = bits.extra_rc;
//has_sidetable_rc 为1说明 extra_rc溢出,存放在sidetable中
if (bits.has_sidetable_rc) {
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
//未优化的isa指针
sidetable_unlock();
return sidetable_retainCount();
}
//优化过的isa,且extra溢出,要执行的方法
size_t
objc_object::sidetable_getExtraRC_nolock(