散列表
结构图
sideTables,其是StripedMap类型,本质是hash表,包含了多张sideTable的集合
static StripedMap<SideTable>& SideTables() {
return SideTablesMap.get();
}
StripedMap中定义了最多能开辟8或者64张表,所以sideTable最多有8或者64张
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif
...
}
sideTable中包含了锁、引用计数表、弱引用表
- spinlock_t 自旋锁
- RefcountMap 引用计数表
- weak_table_t 弱引用表
使用sideTables多张表原因
- 在引用计数处理过程中,经常要开表、关表,要进行加锁、解锁,如果只有一张表,性能会有消耗
- 多张表则保证了内存能及时回收,而不是持续占有空间
每个对象都开一张表,则内存过大,性能效率低
struct SideTable {
spinlock_t slock;
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);
};
流程
首先获取sideTable
- 得到sideTable的weakTable弱引用表
- 创建一个weak_entry_t
- 把referent加入到weak_table的数组inline_referrers
- 把weak_table扩容一下
-
retainCount
获取对象的引用计数大小
首先判断是否为小对象类型,如果是直接return
- 加锁,判断isa.bits是否为nonpointerIsa
- 旧版本中uintptr_t rc = 1 + bits.extra_rc
- 新版本为uintptr_t rc = bits.extra_rc,因为在alloc流程时,initIsa->系统做了操作newisa.extra_rc = 1
- 判断是否有散列表,如果有则将rc+引用计数表的一半值,上篇讲过(存在散列表时,extra_rc和散列表各存储一半值)
弱引用表
- 运行以下代码,引用计数结果为1, 1, 2
- 指针所指向的对象均相同,当weakObjec指向同一片堆内存时,指针个数+1,但不持有改堆对象
- 断点调试上述43行,开启Stack Overflow,Control+单独调试,发现底层调用了objc_initWeak,此过程是LLVM做了处理,绑定到了objc_initWeak
底层源码中,无论objc_initWeak还是objc_destroyWeak都是调用了storeWeak函数,区别是传入的newObj是否为nil
location-> __weak ptr 的地址 ```objectivec id objc_initWeak(id location, id newObj) { if (!newObj) { location = nil; return nil; }
return storeWeak
(location, (objc_object*)newObj); }
void
objc_destroyWeak(id *location)
{
(void)storeWeak
<a name="WWk8K"></a>
#### storeWeak
- 找到散列表中的弱引用表,弱引用表中有很多对象,每个对象又有很多属性,跟关联对象结构类似,二层数据结构表
- storeWeak方法实际上是接收了5个参数,分别是haveOld、haveNew和crashIfDeallocating ,这三个参数都是以模板的方式传入的,是三个bool类型的参数。 分别表示weak指针之前是否指向了一个弱引用,weak指针是否需要指向一个新的引用,若果被弱引用的对象正在析构,此时再弱引用该对象是否应该crash。
- 该方法维护了oldTable 和newTable分别表示旧的引用弱表和新的弱引用表,它们都是SideTable的hash表。
- 如果weak指针之前指向了一个弱引用,则会调用weak_unregister_no_lock 方法将旧的weak指针地址移除。
- 如果weak指针需要指向一个新的引用,则会调用weak_register_no_lock 方法将新的weak指针地址添加到弱引用表中。
- 调用setWeaklyReferenced_nolock 方法修改weak新引用的对象的bit标志位
![未命名文件 (15).jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/21860440/1631171864413-7d47f43b-b927-4073-a26d-bc9dbc3ca652.jpeg#clientId=u61637c94-d0da-4&from=ui&id=u083db7b7&margin=%5Bobject%20Object%5D&name=%E6%9C%AA%E5%91%BD%E5%90%8D%E6%96%87%E4%BB%B6%20%2815%29.jpg&originHeight=1078&originWidth=1683&originalType=binary&ratio=1&size=158313&status=done&style=none&taskId=u1cebb2ff-2339-468a-b1a7-a33569e2869)
<a name="Wk7jc"></a>
#### weak_entry_t
- 结构体类型,包含了referent(对象)和referrers(weak指针)
```objectivec
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;
}
}
};
举例
- 以下打印结果为1 -> 1 -> 2 -> 2 -> 3
- 断点上述45行调试,底层调用了objc_loadWeak -> objc_loadWeakRetained
objc_loadWeakRetained
- 传入了location,即为weak ptr指针
- 取*location,即对象内容
objc_object::rootTryRetain(),即上篇retain操作 ```objectivec id objc_loadWeakRetained(id *location) { id obj; id result; Class cls;
SideTable *table;
retry: // fixme std::atomic this load obj = *location; if (obj->isTaggedPointerOrNil()) return obj;
table = &SideTables()[obj];
table->lock(); if (*location != obj) {
table->unlock();
goto retry;
}
result = obj;
cls = obj->ISA(); if (! cls->hasCustomRR()) {
// Fast case. We know +initialize is complete because
// default-RR can never be set before then.
ASSERT(cls->isInitialized());
if (! obj->rootTryRetain()) {
result = nil;
}
} else {
// Slow case. We must check for +initialize and call it outside
// the lock if necessary in order to avoid deadlocks.
// Use lookUpImpOrForward so we can avoid the assert in
// class_getInstanceMethod, since we intentionally make this
// callout with the lock held.
if (cls->isInitialized() || _thisThreadIsInitializingClass(cls)) {
BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
lookUpImpOrForwardTryCache(obj, @selector(retainWeakReference), cls);
if ((IMP)tryRetain == _objc_msgForward) {
result = nil;
}
else if (! (*tryRetain)(obj, @selector(retainWeakReference))) {
result = nil;
}
}
else {
table->unlock();
class_initialize(cls, obj);
goto retry;
}
}
table->unlock(); return result; } ```
weak_objc管理分离