开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
一、sidetable
直接查看objc源码找到sidetable_addExtraRC_nolock
:
bool
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
ASSERT(isa.nonpointer);
SideTable& table = SideTables()[this];//获取对象的SideTable
size_t& refcntStorage = table.refcnts[this];
size_t oldRefcnt = refcntStorage;
// isa-side bits should not be set here
ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;
uintptr_t carry;
size_t newRefcnt =
addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
if (carry) {
refcntStorage =
SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
return true;
}
else {
refcntStorage = newRefcnt;
return false;
}
}
我们来看下SideTable
的数据结构:
struct SideTable {
spinlock_t slock;//保证线程安全 os_unfair_lock mLock; 是互斥锁
RefcountMap refcnts;//存储对象的引用计数
weak_table_t weak_table;//弱引用表,加锁 解锁 低效
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
void lock() { slock.lock(); }
void unlock() { slock.unlock(); }
void forceReset() { slock.forceReset(); }
// Address-ordered lock discipline for a pair of side tables.
template<HaveOld, HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<HaveOld, HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
在进入SideTables()
的源码:
static StripedMap<SideTable>& SideTables() { //创建获取StripedMap list 真机8个
return SideTablesMap.get();
}
在看下StripedMap:
StripedMap
是用做:缓存带有spinlock_t
锁的能力的类或者是结构体。这个map的个数是固定的,模拟器64个,真机是8个。
大家可以思考一下,在整个项目中只初始化了一个SideTable和所有对象的weak_table_t表。但是这样的效率会很低,因为有spinlock_t加锁,解锁而造成低效的问题
。但是如果每个对象都创建SideTable和weak_table_t表,效率是高了但是内存占用过高
。
apple是怎么解决呢,用了什么方案呢?
来我们直接看StripedMap
部分源码:
PaddedT array[StripeCount];
//核心算法,均匀分配到真机8张表中。
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
方法indexForPointer
通过对象的地址,来确定是在array中的第几个元素,进而拿到哈希值。
而关键的地方在于,这个类对[]
运算符进行了重载,前面取出对应的SideTable
用的方法是SideTables()[this]
,它的翻译过来的实际操作是:SideTables().array[indexForPointer(this)].value
,就是先通过indexForPointer
计算出位置,在通过array[index]取出值,就是对应的SideTable
。
弱引用表 weak_table_t weak_table
直接进入weak_table_t
源码查看:
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
在进入weak_entry_t
源码查看:
struct weak_entry_t {
DisguisedPtr<objc_object> referent;
union {
struct {
weak_referrer_t *referrers;
uintptr_t out_of_line_ness : 2;
uintptr_t num_refs : PTR_MINUS_2;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
bool out_of_line() {
return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
}
weak_entry_t& operator=(const weak_entry_t& other) {
memcpy(this, &other, sizeof(other));
return *this;
}
weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
: referent(newReferent)
{
inline_referrers[0] = newReferrer;
for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
inline_referrers[i] = nil;
}
}
};
weak_entry_t存储着某个对象的弱引用信息,又是一个结构体。跟weak_table_t类似,里面存储一个hash表weak_referrer_t
,弱引用该"对象的指针"的指针。通过weak_referrer_t
的操作,可以使该对象的弱引用指针在对象释放后,置为nil。
DisguisedPtr<objc_object> referent :弱引用对象指针摘要。其实可以理解为弱引用对象的指针,只不过这里使用了摘要的形式存储。(所谓摘要,其实是把地址取负)。
weak_referrer_t
:这是一个共用体,分动态数组和固定长度数组两种情况,
out_of_line
:bool类型区分是weak_referrer_t中数组类型
weak_entry_t& operator=(const weak_entry_t& other)
:赋值
weak_entry_t(objc_object *newReferent, objc_object **newReferrer) 构造
在查看weak_referrer_t *referrers;
的源码实现:
typedef DisguisedPtr<objc_object *> weak_referrer_t;
weak_referrer_t
以指针摘要的形式,存储弱引用指针的指针。weak_referrer_t数组这里设置了两种模式,毕竟动态数组涉及到更多的操作,在小数据量的情况下,使用定长数组效率更高。
总结
iOS的一个项目:会全局维护一个SideTables
,SideTables里面包含多个sidetable
(真机情况下是8个)。
sidetable
中有:spinlock_t(保证线程安全),RefcountMap(存储对象的引用计数),weak_table_t(弱引用表,加锁 解锁 低效) 三个核心属性。
weak_table_t
:中保存着的一个sidetable中所有weak_entries表,从weak_entries中通过对象查找着某个对象对应的弱引用信息weak_entry_t,weak_entry_t中保存着弱引用该对象的 指针地址的hash数组。