散列表
结构图

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_SIMULATORenum { StripeCount = 8 };#elseenum { 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标志位<a name="Wk7jc"></a>#### weak_entry_t- 结构体类型,包含了referent(对象)和referrers(weak指针)```objectivecstruct 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管理分离

