摘要
ObjC 引用计数,以哈希表形式,存在于全局的几个 SideTable 之中。
而 Swift 则是对象自行保存着引用计数的关系。
ObjC
引用计数表数据结构:obj 作为 key,计数作为 value。
以 RefcountMap 的结构,存储在 SideTable 中,而后者则是使用 StripedMap 进行管理。
(关于 StripedMap 可查看 Objc StripedMap 优化加锁缓存)
相关源码如下。
SideTable 中的 refcnts 即为引用计数。
struct SideTable {
spinlock_t slock; // 自旋锁
RefcountMap refcnts; // 引用计数
weak_table_t weak_table;
...
}
而 RefcountMap 实际就是 DenseMapBase。
// key 是 DisguisedPtr<objc_object>, value 是 size_t
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
// DenseMap 的父类
class DenseMapBase {
protected:
typedef std::pair<KeyT, ValueT> BucketT;
...
}
我们可以将 DenseMapBase 简单地理解为「哈希表」即可。
当然它远不只这么简单,更多内容可看文末链接。
SideTable_retain()
引用计数表是如何更新的?
从 SideTable_retain() 入手,可看到先获取全局的 SideTables(),然后从中获取引用计数,再增加。
static StripedMap<SideTable>& SideTables() {
return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
id
objc_object::SideTable_retain()
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
table.lock();
size_t& refcntStorage = table.refcnts[this];
if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
refcntStorage += SIDE_TABLE_RC_ONE;
}
table.unlock();
return (id)this;
}
其调用堆栈:
而触发 objc_object::retain 有以下场景
Swift
与 ObjC 不同,Swift 中的对象 (HeapObject) 直接保存着引用关系。
swift/stdlib/public/SwiftShims/RefCount.h 的注释,简明概要地说明了引用计数是如何保存的。
/*
Strong and unowned variables point at the object.
Weak variables point at the object's side table.
Storage layout:
HeapObject {
isa
InlineRefCounts {
atomic<InlineRefCountBits> {
strong RC + unowned RC + flags
OR
HeapObjectSideTableEntry*
}
}
}
HeapObjectSideTableEntry {
SideTableRefCounts {
object pointer
atomic<SideTableRefCountBits> {
strong RC + unowned RC + weak RC + flags
}
}
}
*/
来看看相关源码。
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *metadata;
// typedef swift::RefCounts<swift::InlineRefCountBits> swift::InlineRefCounts
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
...
}
// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts;
从以上代码,可以看到 InlineRefCountBits 与 SideTableRefCountBits 其实都是 RefCounts。
两者的声明:
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
class alignas(sizeof(void*) * 2) SideTableRefCountBits : public RefCountBitsT<RefCountNotInline>;
而以下的注释,再次强调了引用计数的存储位置。
// RefCountIsInline: refcount stored in an object
// RefCountNotInline: refcount stored in an object's side table entry
enum RefCountInlinedness {RefCountNotInline = false, RefCountIsInline = true};
swift_retain()
同样地,来看看 Swift 是如何更新引用计数的。
入口函数:
static HeapObject *_swift_retain_(HeapObject *object) {
SWIFT_RT_TRACK_INVOCATION(object, swift_retain);
if (isValidPointerForNativeRetain(object))
object->refCounts.increment(1);
return object;
}
增加引用计数的方法。
template <typename RefCountBits>
class RefCounts {
// Increment the reference count.
void increment(uint32_t inc = 1) {
...
RefCountBits newbits;
do {
newbits = oldbits;
bool fast = newbits.incrementStrongExtraRefCount(inc);
// 无 weak、unowned 引用时一般不会进入。
if (SWIFT_UNLIKELY(!fast)) {
if (oldbits.isImmortal(false))
return;
return incrementSlow(oldbits, inc);
}
} while (!refCounts.compare_exchange_weak(oldbits, newbits,
std::memory_order_relaxed));
}
一般的增加引用计数:
// Returns true if the increment is a fast-path result.
// Returns false if the increment should fall back to some slow path
// (for example, because UseSlowRC is set or because the refcount overflowed).
SWIFT_NODISCARD SWIFT_ALWAYS_INLINE bool
incrementStrongExtraRefCount(uint32_t inc) {
// This deliberately overflows into the UseSlowRC field.
bits += BitsType(inc) << Offsets::StrongExtraRefCountShift;
return (SignedBitsType(bits) >= 0);
}
使用 HeapObjectSideTableEntry 的情况:
class RefCounts {
// 当有 weak、unowned 引用时,才会调用
template <typename RefCountBits>
void RefCounts<RefCountBits>::incrementSlow(RefCountBits oldbits,
uint32_t n) {
if (oldbits.isImmortal(false)) {
return;
}
else if (oldbits.hasSideTable()) {
// Out-of-line slow path.
auto side = oldbits.getSideTable();
// 调用的是 void HeapObjectSideTableEntry::incrementStrong(uint32_t inc) 方法
side->incrementStrong(n);
}
else {
// Retain count overflow.
swift::swift_abortRetainOverflow();
}
}
}
// void HeapObjectSideTableEntry::incrementStrong(uint32_t inc)
void incrementStrong(uint32_t inc) {
refCounts.increment(inc);
}
小结
ObjC 引用计数关系,以哈希表形式,存在于全局的几个 SideTable 之中。
Swift 则是对象自身保存着引用计数的关系。
比较特殊的是,当被 weak 或 unowned 引用时,对象会被分配一个 SideTable,后者用于保存引用关系。
相比之下,Swift 的方案,因为没有哈希计算,增删改查的效率都更高些。