iOS-散列表
作者:互联网
一、散列表sideTable的数据结构
SideTable主要包含spinlock锁,引用计数表(存放从extra_rc接收的一半引用计数),弱引用表。
truct SideTable { spinlock_t slock; // 存放从extra_rc接收的那一半引用计数 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); };
1.1 spinlock_t 加锁
spinlock_t 不是自旋锁,在底层代码查找的过程中,我们可以发现他是一把os_unfair_lock锁,在使用sidetable的时候,频繁的读取需要加锁,一张表无疑影响了效率,因此,我们采用stripedMap来分散压力,且stripedMap的数量是根据系统来确定的(真机模式下sidetable最多为8张)。
// 上面 SideTables 的实现 static StripedMap<SideTable>& SideTables() { return SideTablesMap.get(); }
1.2RefcountMap(引用计数表)
- RefcountMap本身从DenseMap得来
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
- 存放从extra_rc接收的那一半引用计数
if (variant == RRVariant::Full) { if (slowpath(transcribeToSideTable)) { // Copy the other half of the retain counts to the side table. // 将引用计数一半存在散列表中的方法 sidetable_addExtraRC_nolock(RC_HALF); } if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock(); } else { ASSERT(!transcribeToSideTable); ASSERT(!sideTableLocked); }
接着,来看一下 sidetable_addExtraRC_nolock 方法:
bool objc_object::sidetable_addExtraRC_nolock(size_t delta_rc) { ASSERT(isa.nonpointer); // 获取SideTables,也就是StripeMap SideTable& table = SideTables()[this]; 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; } }
1.3 WeakTable(弱引用表)
弱引用底层调用objc_initWeak:
id objc_initWeak(id *location, id newObj) { if (!newObj) { *location = nil; return nil; } return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating> (location, (objc_object*)newObj); }
1.3.1 storeWeak(添加引用的时候调用)
- storeWeak一共有五个参数,其中3个参数定义在了template模版,
- weak_unregister_no_lock:清除原来弱引用表中的数据
- weak_register_no_lock:将weak的指针地址添加到对象的弱引用表
enum CrashIfDeallocating { DontCrashIfDeallocating = false, DoCrashIfDeallocating = true }; // HaveOld:weak指针是否指向一个弱引用 // HavNew:weak指针是否需要指向一个新的引用 template <HaveOld haveOld, HaveNew haveNew, enum CrashIfDeallocating crashIfDeallocating> static id storeWeak(id *location, objc_object *newObj) { ASSERT(haveOld || haveNew); if (!haveNew) ASSERT(newObj == nil); Class previouslyInitializedClass = nil; id oldObj; SideTable *oldTable; SideTable *newTable; // Acquire locks for old and new values. // Order by lock address to prevent lock ordering problems. // Retry if the old value changes underneath us. retry: if (haveOld) { oldObj = *location; oldTable = &SideTables()[oldObj]; } else { oldTable = nil; } if (haveNew) { newTable = &SideTables()[newObj]; } else { newTable = nil; } SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable); if (haveOld && *location != oldObj) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); goto retry; } // Prevent a deadlock between the weak reference machinery // and the +initialize machinery by ensuring that no // weakly-referenced object has an un-+initialized isa. if (haveNew && newObj) { Class cls = newObj->getIsa(); if (cls != previouslyInitializedClass && !((objc_class *)cls)->isInitialized()) { SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); class_initialize(cls, (id)newObj); // If this class is finished with +initialize then we're good. // If this class is still running +initialize on this thread // (i.e. +initialize called storeWeak on an instance of itself) // then we may proceed but it will appear initializing and // not yet initialized to the check above. // Instead set previouslyInitializedClass to recognize it on retry. previouslyInitializedClass = cls; goto retry; } } // Clean up old value, if any. if (haveOld) { // 清除原来弱引用表中数据 weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); } // Assign new value, if any. if (haveNew) { newObj = (objc_object *) // 将weak的指针地址添加到对象的弱引用表 weak_register_no_lock(&newTable->weak_table, (id)newObj, location, crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating); // weak_register_no_lock returns nil if weak store should be rejected // Set is-weakly-referenced bit in refcount table. if (!newObj->isTaggedPointerOrNil()) { // 将对象曾经指向过若引用的标识置为true,没有弱引用的释放更快 newObj->setWeaklyReferenced_nolock(); } // Do not set *location anywhere else. That would introduce a race. *location = (id)newObj; } else { // No new value. The storage is not changed. } SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable); // This must be called without the locks held, as it can invoke // arbitrary code. In particular, even if _setWeaklyReferenced // is not implemented, resolveInstanceMethod: may be, and may // call back into the weak reference machinery. callSetWeaklyReferenced((id)newObj); return (id)newObj; }
2.2 weak_entry_t
struct weak_table_t { // 弱引用数组 weak_entry_t *weak_entries; // 数组个数 size_t num_entries; uintptr_t mask; uintptr_t max_hash_displacement; };
weak_entries是一个哈希数组,一个对象可以被多个弱引用指针引用,因此,这里用数组的形式表示一个对象的多个弱引用;数组中存储的内容就是弱引用对象的指针地址。当对象的弱引用个数小于等于4时走静态存储(在weak_entry_t初始化的时候一并分配好),大于4走动态存储。
struct weak_entry_t { DisguisedPtr<objc_object> referent; // 联合体,对象的弱引用个数若小于等于4,下边的结构体:静态存储,大于4,上边的结构体:动态存储 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; } } };
三、总结
- sidetables可以理解为一个全局的hash数组,里面存储了sidetables类型的数据,其中长度为8或者64
- 一个obj(oc对象)对应了一个sideTable,但是一个SideTable,会对应多个obj,因为sidetabels的数量只有8或者64个,所以有很多obj会共用一个sidetable
- 在弱引用表中,key是对象的地址,value是weak指针地址的数组(weak_entry_t)
- weak_unregister_no_lock 和 weak_register_no_lock 中都是对 weak_entry_t 类型的数组进行操作
(图片来自:https://juejin.cn/post/7113939858665504776#heading-2)
标签:SideTable,iOS,weak,newObj,引用,lock,列表,id 来源: https://www.cnblogs.com/SNMX/p/16426748.html