- 在阐述weak底层实现原理之前,首先介绍几个重要的数据结构;
SideTables散列表集合
static StripedMap& SideTables() {
return SideTablesMap.get();
}
template
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif
struct PaddedT {
T value alignas(CacheLineSize);
};
PaddedT array[StripeCount];
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
const T& operator[] (const void *p) const {
return const_cast>(this)[p];
}
// Shortcuts for StripedMaps of locks.
void lockAll() {
for (unsigned int i = 0; i < StripeCount; i++) {
array[i].value.lock();
}
}
void unlockAll() {
for (unsigned int i = 0; i < StripeCount; i++) {
array[i].value.unlock();
}
}
void forceResetAll() {
for (unsigned int i = 0; i < StripeCount; i++) {
array[i].value.forceReset();
}
}
void defineLockOrder() {
for (unsigned int i = 1; i < StripeCount; i++) {
lockdebug_lock_precedes_lock(&array[i-1].value, &array[i].value);
}
}
void precedeLock(const void *newlock) {
// assumes defineLockOrder is also called
lockdebug_lock_precedes_lock(&array[StripeCount-1].value, newlock);
}
void succeedLock(const void *oldlock) {
// assumes defineLockOrder is also called
lockdebug_lock_precedes_lock(oldlock, &array[0].value);
}
const void *getLock(int i) {
if (i < StripeCount) return &array[i].value;
else return nil;
}
#if DEBUG
StripedMap() {
// Verify alignment expectations.
uintptr_t base = (uintptr_t)&array[0].value;
uintptr_t delta = (uintptr_t)&array[1].value - base;
ASSERT(delta % CacheLineSize == 0);
ASSERT(base % CacheLineSize == 0);
}
#else
constexpr StripedMap() {}
#endif
};
- SideTables顾名思义是存储sideTable散列表的集合,其类型为
StripedMap
属于哈希表;
-
StripeCount = 8
表明在iOS或者非iOS模拟器上,最多会同时存在8张sideTable散列表;
-
static unsigned int indexForPointer(const void *p)
:根据对象获取对应的sideTable散列表 indexForPointer
本质就是一个哈希函数,根据对象获取哈希索引值index;
sideTable散列表
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
static void lockTwo(SideTable *lock1, SideTable *lock2);
template
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
-
spinlock_t slock
:线程锁保证散列表的访问是线程安全的;
-
RefcountMap refcnts
:引用计数表,本质也是一个哈希表,类型如下:
typedef objc::DenseMap,size_t,RefcountMapValuePurgeable> RefcountMap;
- 可以看出RefcountMap存储的键值对为:
<对象,对象的引用计数值>
-
weak_table_t
:弱引用表,是一个全局弱引用表,其结构如下所示:
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
-
weak_entry_t *weak_entries
:存储weak_entry_t结构体的数组;
-
size_t num_entries
:表示存储weak_entries数组中存储weak_entry_t的数量;
-
uintptr_t mask
:哈希数组的总长度减 1,会参与 hash 函数计算,保证计算出来的哈希索引index不会越界;
weak_entry_t结构体
- weak_entry_t是保存某个对象的
所有弱引用(weak)
的结构体,目标对象可以被多个弱引用指向,weak_entry_t的其底层结构如下:
struct weak_entry_t {
DisguisedPtr 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;
}
}
};
-
DisguisedPtr referent
:是被弱引用指向的目标对象;
- 接下来看到一个联合体,当外界指向目标对象
referent
的弱引用指针小于等于WEAK_INLINE_COUNT = 4时,使用 inline_referrers
定长数组保存这些弱引用指针;
- 当外界指向目标对象
referent
的弱引用指针大于4时,使用weak_referrer_t *referrers
哈希表存储弱引用指针;
-
bool out_of_line()
:返回YES时表示使用weak_referrer_t *referrers
哈希表存储弱引用指针,返回NO时表示使用 inline_referrers
定长数组存储弱引用指针;
- 本文是在
objc_818.2源码工程
的基础上进行分析的,新建类YYPserson
,调试工程如下所示:
- 创建并初始化实例对象person,并使用
__weak
弱引用指针进行修饰;底层会调用objc_initWeak
函数;其实现如下:
id objc_initWeak(id *location, id newObj){
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak
(location, (objc_object*)newObj);
}
template
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(oldTable, newTable);
if (haveOld && *location != oldObj) {
SideTable::unlockTwo(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.
//确保目标对象的类class,要被初始化。
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized()) {
SideTable::unlockTwo(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.
//weak指针已指向其他对象,需移除
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.
//weak指针指向新的对象,需注册
if (haveNew) {
newObj = (objc_object *)
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()) {
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(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;
}
- 若weak引用指针,之前指向一个旧的对象,现在要指向一个新的对象,那么旧对象的弱引用表要移除weak指针,新对象的弱引用表要新增weak指针;
-
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location)
:旧对象移除弱引用指针,内部实现如下:
/**
* Unregister an already-registered weak reference.
* This is used when referrer's storage is about to go away, but referent
* isn't dead yet. (Otherwise, zeroing referrer later would be a
* bad memory access.)
* Does nothing if referent/referrer is not a currently active weak reference.
* Does not zero referrer.
*
* FIXME currently requires old referent value to be passed in (lame)
* FIXME unregistration should be automatic if referrer is collected
*
* @param weak_table The global weak table.
* @param referent The object.
* @param referrer The weak reference.
*/
void weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id){
//目标对象
objc_object *referent = (objc_object *)referent_id;
//弱引用指针
objc_object **referrer = (objc_object **)referrer_id;
//weak_entry_t结构体
weak_entry_t *entry;
if (!referent) return;
//根据对象在弱引用表中获取weak_entry_t结构体,
if ((entry = weak_entry_for_referent(weak_table, referent))) {
//若存在weak_entry_t结构体,在weak_entry_t中删除弱引用指针
remove_referrer(entry, referrer);
//若weak_entry_t结构体中的弱引用指针为空,删除weak_entry_t结构体
bool empty = true;
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}else {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
if (empty) {
weak_entry_remove(weak_table, entry);
}
}
// Do not set *referrer = nil. objc_storeWeak() requires that the
// value not change.
}
- 根据目标对象
referent
,在弱引用表weak_table_t
中获取到weak_entry_t
结构体,然后在weak_entry_t
结构体中删除弱引用指针referrer
,即调用remove_referrer(entry, referrer)
函数,实现如下:
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
//弱引用指针<=4,存储在inline_referrers数组中
if (! entry->out_of_line()) {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == old_referrer) {
entry->inline_referrers[i] = nil;
return;
}
}
_objc_inform("Attempted to unregister unknown __weak variable "
"at %p. This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
old_referrer);
objc_weak_error();
return;
}
//哈希函数计算出弱引用指针在哈希表中的index
size_t begin = w_hash_pointer(old_referrer) & (entry->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != old_referrer) {
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);
hash_displacement++;
if (hash_displacement > entry->max_hash_displacement) {
_objc_inform("Attempted to unregister unknown __weak variable "
"at %p. This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
old_referrer);
objc_weak_error();
return;
}
}
//弱引用指针>4,存储在referrers哈希表中
entry->referrers[index] = nil;
entry->num_refs--;
}
- 删除弱引用指针分为两种情况:1.弱引用指针<=4,存储在inline_referrers数组中,2.弱引用指针>4,存储在referrers哈希表中;
- 在删除完目标对象的弱引用指针之后,会判断weak_entry_t结构体中的弱引用指针数量是否为空,如果为空则删除weak_entry_t结构体,执行
weak_entry_remove(weak_table, entry)
函数;
-
weak_register_no_lock
:新对象注册弱引用指针,实现如下:
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, WeakRegisterDeallocatingOptions deallocatingOptions)
{
//目标对象
objc_object *referent = (objc_object *)referent_id;
//弱引用指针
objc_object **referrer = (objc_object **)referrer_id;
if (referent->isTaggedPointerOrNil()) return referent_id;
// ensure that the referenced object is viable
if (deallocatingOptions == ReturnNilIfDeallocating ||
deallocatingOptions == CrashIfDeallocating) {
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
// Use lookUpImpOrForward so we can avoid the assert in
// class_getInstanceMethod, since we intentionally make this
// callout with the lock held.
auto allowsWeakReference = (BOOL(*)(objc_object *, SEL))
lookUpImpOrForwardTryCache((id)referent, @selector(allowsWeakReference),
referent->getIsa());
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, @selector(allowsWeakReference));
}
if (deallocating) {
if (deallocatingOptions == CrashIfDeallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
} else {
return nil;
}
}
}
// now remember it and where it is being stored
weak_entry_t *entry;
//在weak_table_t中weak_entry_t结构体存在,
if ((entry = weak_entry_for_referent(weak_table, referent))) {
//往weak_entry_t结构体中插入referrer弱引用指针
append_referrer(entry, referrer);
} else {
//在weak_table_t中weak_entry_t结构体不存在,
//创建一个新的weak_entry_t结构体
weak_entry_t new_entry(referent, referrer);
//weak_table_t的扩容
weak_grow_maybe(weak_table);
//往weak_table中插入new_entry
weak_entry_insert(weak_table, &new_entry);
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
- 首先根据目标对象
referent
,在弱引用表中weak_table_t
,查找weak_entry_t
结构体,若weak_entry_t存在,则往weak_entry_t中插入弱引用指针referrer
,执行append_referrer
函数,weak_entry_t结构体内部插入referrer
也分为两种情况,联合体结构,若weak_entry_t不存在,则创建一个新的weak_entry_t结构体,然后weak_table_t
扩容,最后插入新建的weak_entry_t结构体;
- 当YYPerson对象在销毁时,调用dealloc方法,底层会清空弱引用表中的所有弱引用指针,有效的避免了野指针造成的崩溃,dealloc方法的底层实现见 iOS内存管理07 -- retain, release, dealloc与retainCount的源码分析