文章目录
- isa简单了解
- isa的bits成员变量
- isa的cls成员变量
- 总结
- sideTables、sideTable 简单了解
- retainCount实现
- retain 实现
- release实现
- retain、release过程大总结:
- objc_autoreleaseReturnValue实现
- objc_retainAutoreleasedReturnValue实现
内容会陆续补充
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()
{
ASSERT(isa.nonpointer);
//SideTables中通过this指针(key)获取sideTable
SideTable& table = SideTables()[this];
//在sidetable中的refcnts(存储引用计数的哈希表)通过this(key)获取对象的引用计数(value)
RefcountMap::iterator it = table.refcnts.find(this);
if (it == table.refcnts.end()) return 0;
//返回引用计数
else return it->second >> SIDE_TABLE_RC_SHIFT;
}
//未优化的isa
uintptr_t
objc_object::sidetable_retainCount()
{
SideTable& table = SideTables()[this];
size_t refcnt_result = 1;
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
// this is valid for SIDE_TABLE_RC_PINNED too
refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
}
table.unlock();
return refcnt_result;
}
retain 实现
objc_retain(id obj)
{
if (obj->isTaggedPointerOrNil()) return obj;
return obj->retain();
}
objc_object::retain()
{
ASSERT(!isTaggedPointer());
return rootRetain(false, RRVariant::FastOrMsgSend);
}
objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant)
{
//如果是标签指针 直接返回
if (slowpath(isTaggedPointer())) return (id)this;
bool sideTableLocked = false;
bool transcribeToSideTable = false;
isa_t oldisa;
isa_t newisa;
oldisa = LoadExclusive(&isa.bits);
if (variant == RRVariant::FastOrMsgSend) {
// These checks are only meaningful for objc_retain()
// They are here so that we avoid a re-load of the isa.
if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
ClearExclusive(&isa.bits);
if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
return swiftRetain.load(memory_order_relaxed)((id)this);
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
}
}
if (slowpath(!oldisa.nonpointer)) {
// a Class is a Class forever, so we can perform this check once
// outside of the CAS loop
if (oldisa.getDecodedClass(false)->isMetaClass()) {
ClearExclusive(&isa.bits);
return (id)this;
}
}
do {
transcribeToSideTable = false;
newisa = oldisa;
//未优化的isa部分
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
else return sidetable_retain(sideTableLocked);
}
//对象正在被释放的处理
// don't check newisa.fast_rr; we already called any RR overrides
if (slowpath(newisa.isDeallocating())) {
ClearExclusive(&isa.bits);
if (sideTableLocked) {
ASSERT(variant == RRVariant::Full);
sidetable_unlock();
}
if (slowpath(tryRetain)) {
return nil;
} else {
return (id)this;
}
}
//extra_rc未溢出 extra_rc++
uintptr_t carry;
newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++
//printf("------------------%d\n", newisa.extra_rc);
//newisa.extra_rc++ 溢出
if (slowpath(carry)) {
// newisa.extra_rc++ overflowed
if (variant != RRVariant::Full) {
ClearExclusive(&isa.bits);
//重新调rootRetain,handleOverflow 为 true (处理溢出)
return rootRetain_overflow(tryRetain);
}
// Leave half of the retain counts inline and
// prepare to copy the other half to the side table.
// 保留一半引用计数
// 准备将另一半复制到 side table.
if (!tryRetain && !sideTableLocked) sidetable_lock();
sideTableLocked = true;
transcribeToSideTable = true;
newisa.extra_rc = RC_HALF;
//has_sidetable_rc设置为true,表示使用了sidetable来计算引用次数
newisa.has_sidetable_rc = true;
}
// 更新 isa 值
} while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
if (variant == RRVariant::Full) {
if (slowpath(transcribeToSideTable)) {
// Copy the other half of the retain counts to the side table.
// 将另一半复制到 side table side table.
sidetable_addExtraRC_nolock(RC_HALF);
}
if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
} else {
ASSERT(!transcribeToSideTable);
ASSERT(!sideTableLocked);
}
return (id)this;
}
- TaggedPointer:值存在指针内,直接返回。
- !newisa.nonpointer:未优化的 isa ,使用sidetable_retain()。
- newisa.nonpointer:已优化的 isa , 分 extra_rc 溢出和未溢出的两种情况。
未溢出时,isa.extra_rc++。
溢出时,将 isa.extra_rc 中一半值转移至sidetable中,将isa.has_sidetable_rc设置为true,表示使用了sidetable来存引用次数。
release实现
- (oneway void)release {
_objc_rootRelease(self);
}
_objc_rootRelease(id obj)
{
ASSERT(obj);
obj->rootRelease();
}
objc_object::rootRelease()
{
return rootRelease(true, RRVariant::Fast);
}
objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
{
//是标记指针 直接返回
if (slowpath(isTaggedPointer())) return false;
bool sideTableLocked = false;
isa_t newisa, oldisa;
oldisa = LoadExclusive(&isa.bits);
if (variant == RRVariant::FastOrMsgSend) {
// These checks are only meaningful for objc_release()
// They are here so that we avoid a re-load of the isa.
if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
ClearExclusive(&isa.bits);
if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
swiftRelease.load(memory_order_relaxed)((id)this);
return true;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
return true;
}
}
if (slowpath(!oldisa.nonpointer)) {
// a Class is a Class forever, so we can perform this check once
// outside of the CAS loop
if (oldisa.getDecodedClass(false)->isMetaClass()) {
ClearExclusive(&isa.bits);
return false;
}
}
retry:
do {
newisa = oldisa;
//没优化过的指针
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
return sidetable_release(sideTableLocked, performDealloc);
}
//对象正在被销毁
if (slowpath(newisa.isDeallocating())) {
ClearExclusive(&isa.bits);
if (sideTableLocked) {
ASSERT(variant == RRVariant::Full);
sidetable_unlock();
}
return false;
}
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
// extra_rc--
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);
if (slowpath(carry)) {
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
if (slowpath(newisa.isDeallocating()))
goto deallocate;
if (variant == RRVariant::Full) {
if (slowpath(sideTableLocked)) sidetable_unlock();
} else {
ASSERT(!sideTableLocked);
}
return false;
underflow:
//处理下溢,从 side table 中借位或者释放
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
//has_sidetable_rc设置为1,则使用了sideTable
if (slowpath(newisa.has_sidetable_rc)) {
if (variant != RRVariant::Full) {
// 调用本函数处理下溢
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
oldisa = LoadExclusive(&isa.bits);
goto retry;
}
// Try to remove some retain counts from the side table.
// 从 sidetable 中借位引用计数给 extra_rc
auto borrow = sidetable_subExtraRC_nolock(RC_HALF);
// we'll clear the side table if no refcounts remain there
//sideTable没有引用计数 就清空
bool emptySideTable = borrow.remaining == 0;
if (borrow.borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
bool didTransitionToDeallocating = false;
// extra_rc 是计算额外的引用计数,0 即表示被引用一次?????
newisa.extra_rc = borrow.borrowed - 1; // redo the original decrement too
newisa.has_sidetable_rc = !emptySideTable;
bool stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);
// 保存失败,恢复现场,重试
if (!stored && oldisa.nonpointer) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
uintptr_t overflow;
newisa.bits =
addc(oldisa.bits, RC_ONE * (borrow.borrowed-1), 0, &overflow);
newisa.has_sidetable_rc = !emptySideTable;
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);
if (stored) {
didTransitionToDeallocating = newisa.isDeallocating();
}
}
}
//如果还是保存失败,则还回 side table
if (!stored) {
// Inline update failed.
// Put the retains back in the side table.
ClearExclusive(&isa.bits);
sidetable_addExtraRC_nolock(borrow.borrowed);
oldisa = LoadExclusive(&isa.bits);
goto retry;
}
// Decrement successful after borrowing from side table.
if (emptySideTable)
sidetable_clearExtraRC_nolock();
if (!didTransitionToDeallocating) {
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
}
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
deallocate:
// Really deallocate.
ASSERT(newisa.isDeallocating());
ASSERT(isa.isDeallocating());
if (slowpath(sideTableLocked)) sidetable_unlock();
__c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return true;
}
逻辑大概如下:
- 是标记指针,直接返回
- 是未优化过的isa,执行sidetable_release
- 优化过的指针:
extra_rc未下溢,extra_rc–
extra_rc下溢:从 sidetable 中借位给 extra_rc 达到半满,如果无法借位则说明引用计数归零需要进行释放。其中借位时可能保存失败会不断重试
retain、release过程大总结:
- 标记指针,直接返回
- 对于没有优化过的isa
retain就调用sidetable_retain,对sideTable中存储的引用计数进行操作。
release就调用sidetable_release,对sideTable中存储的引用计数进行操作。 - 对于优化过的isa
retain,如果extra_rc未溢出,ectra_rc++;溢出,将isa.has_sidetable_rc设置为true,表示sideTable存储引用计数,将extra_rc一半值移到sideTable中。
release,如果extra_rc为下溢,extra_rc–;下溢,从 sidetable 中借位给 extra_rc ,如果无法借位则说明引用计数归零需要进行释放。
retain实现逻辑图
release实现逻辑图
objc_autoreleaseReturnValue实现
//
enum ReturnDisposition : bool {
ReturnAtPlus0 = false, ReturnAtPlus1 = true
};
id
objc_autoreleaseReturnValue(id obj)
{
//优化 ,returnAtPlus1表示true
if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;
//不优化
return objc_autorelease(obj);
}
static ALWAYS_INLINE bool
prepareOptimizedReturn(ReturnDisposition disposition)
{
ASSERT(getReturnDisposition() == ReturnAtPlus0);
//检查调用方的方法列表,如果紧跟着执行retain,将不注册到autoreleasePool中
if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {
//设置标记
if (disposition) setReturnDisposition(disposition);
return true;
}
return false;
}
//ReturnAtPlus0 = false, ReturnAtPlus1 = true,
//优化流程将一个标志位存储在 TLS (Thread Local Storage) 中
//设置标志位
static ALWAYS_INLINE void
setReturnDisposition(ReturnDisposition disposition)
{
tls_set_direct(RETURN_DISPOSITION_KEY, (void*)(uintptr_t)disposition);
}
代码逻辑:
- 检查调用方方法列表是否紧跟着retain
- 紧跟着retain,设置TLS中的标志位为true,返回对象
- 没有retain,执行objc_autorelease,放入自动释放池
objc_retainAutoreleasedReturnValue实现
id
objc_retainAutoreleasedReturnValue(id obj)
{
// 如果 TLS 中标记表示使用了优化程序,则直接返回
if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;
return objc_retain(obj);
}
static ALWAYS_INLINE ReturnDisposition
acceptOptimizedReturn()
{
//取出标记
ReturnDisposition disposition = getReturnDisposition();
//重置标志位 置为false
setReturnDisposition(ReturnAtPlus0); // reset to the unoptimized state
return disposition;
}
//获取标记的函数
static ALWAYS_INLINE ReturnDisposition
getReturnDisposition()
{
return (ReturnDisposition)(uintptr_t)tls_get_direct(RETURN_DISPOSITION_KEY);
}
//ReturnAtPlus0 = false, ReturnAtPlus1 = true,
//优化流程将一个标志位存储在 TLS (Thread Local Storage) 中
static ALWAYS_INLINE void
setReturnDisposition(ReturnDisposition disposition)
{
tls_set_direct(RETURN_DISPOSITION_KEY, (void*)(uintptr_t)disposition);
}
代码逻辑:
- 取出TLS 中标记
- 重置标志位,置为false
- 根据取出的标记判断是否执行优化
执行优化,直接返回对象
不执行优化,引用计数+1