1. weak关键字
用weak
关键字修饰的对象指针是弱引用,被引用对象的引用计数不会+1,并在引用对象被释放
的时候自动被设置为nil
。通常用于解决循环引用
问题,如代理以及及block
的使用中,相对会较多的使用到weak
。
为了能够更深入的理解 weak
的底层实现,应对面试问到关于weak
实现的问题,这次记录并学习一下 weak
实现的源码。
2. 实现的大概流程
我们从下面这段代码开始,来学习一下weak
实现的源码:
{
NSObject *obj = [[NSObject alloc] init];
id __weak obj1 = obj;
}
创建weak
引用的时候,会走到runtime
的 objc_initWeak
的这个方法里,可以通过符号断点进行验证,源码如下:
// location 是指向对象的弱指针的地址
// newObj 是对象指针
id objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
从上面可以看到会走到storeWeak
方法中,源码如下:
/*
*location : weak指针的地址
newObj : 被指向的对象
*/
template <HaveOld haveOld, HaveNew haveNew,
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;
retry:
if (haveOld) {
// 如果weak 指针之前指向过其他对象,取出这个旧对象
oldObj = *location;
// 以旧对象为 key,从全局 SideTables()中取出旧对象对应的 SideTable
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (haveNew) {
// 如果有新值,那么就取出新值对应的 SideTable
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
// 保证弱引用的对象的 isa 非空,防止弱引用机制和+initialize发生死锁
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);
previouslyInitializedClass = cls;
goto retry;
}
}
// 如果之前weak 指针指向了别的对象,这里清空
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
if (haveNew) {
// 通过 newObj 和 location 生成一个新的 weak_entry_t并插入到 newObj 的弱引用数组中
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
// Set is-weakly-referenced bit in refcount table
if (newObj && !newObj->isTaggedPointer()) {
// 设置被引用对象 isa 的弱引用标志位为 YES
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);
return (id)newObj;
}
从上面这个方法中我们可以大概了解了围绕 weak
指针进行的一些操作:
- 从全局
SideTables()
中获取对象所在的SideTable
-
isa
的非空校验,如果isa
没有被初始化,则执行class_initialize(cls, (id)newObj)
方法 - 如果
weak 指针
之前指向了别的对象,就解除对旧对象的引用
- 注册新对象的弱引用
- 设置新对象的
弱引用标志符为 YES
大概的流程图如下:
3. 散列表的相关结构
上面storeWeak
方法中,我们看到有一个全局的 SideTables()
散列表,我们以此为切入点,去查看一下关于weak
实现涉及到的整体结构。
3.1 SideTables()
首先,SideTables()
是一个静态函数,代码如下:
static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;
static StripedMap<SideTable>& SideTables() {
return SideTablesMap.get();
}
函数体里面调用了一个全局的静态变量SideTablesMap
的 get()
方法,这个静态变量保存了所有的SideTable
,是objc
命名空间下的一个ExplicitInit
类,它里面实现了get()
方法,如下:
Type &get() {
return *reinterpret_cast<Type *>(_storage);
}
这个get()
方法其实返回的就是StripedMap
类的实例。
3.2 StripedMap
StripedMap 是一个以void *p
为 key,PaddedT
为 value 的表,实现如下:
template<typename T>
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 }; // 真机下StripedMap存储的 SideTable 数量为 8
#else
enum { StripeCount = 64 }; // 模拟器下为 64
#endif
// 对于SideTable的封装
struct PaddedT {
T value alignas(CacheLineSize);
};
// 存储 PaddedT 的散列表
PaddedT array[StripeCount];
// 散列函数,通过对象地址计算出对应 PaddedT在数组中的下标
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
// 取值操作,重写了[ ]方法,上面提到的&SideTables()[oldObj]会调用到这个方法
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
const T& operator[] (const void *p) const {
return const_cast<StripedMap<T>>(this)[p];
}
// ... 省略一些方法
};
综上可以得出 SideTables
的结构实际上如下图所示:
3.3 SideTable
上面介绍StripedMap
时,其内部有一个哈希表,表中存储的是 PaddedT
结构体,结构体的 value
就是 SideTable
,其实现如下:
struct SideTable {
spinlock_t slock; // 保证原子操作的自旋锁
RefcountMap refcnts; // 引用计数的 hash 表
weak_table_t weak_table; // weak 引用全局 hash 表
// 构造函数
SideTable() {
memset(&weak_table, 0, sizeof(weak_table));
}
// 析构函数
~SideTable() {
_objc_fatal("Do not delete SideTable.");
}
};
我们现在把重点放在weak_table_t weak_table
上。
SideTable
结构图如下:
3.4 weak_table_t
上面我们可以看到SideTable
结构体中有一个weak_table_t
结构体类型的成员变量,实现如下:
// 全局弱引用表
struct weak_table_t {
weak_entry_t *weak_entries; // hash 数组,用来存储弱引用对象相关信息的 weak_entry_t
size_t num_entries; // hash数组中元素的个数
uintptr_t mask; // hash 数组的长度(并不是实际的存储个数)-1,主要用来参与哈希函数
uintptr_t max_hash_displacement; // 最大哈希偏移值
};
结构图如下:
3.5 weak_entry_t
上面的weak_table_t
中可以看到其内部有一个weak_entry_t
的结构体数组,这就是其内部维护的哈希表,我们现在来看下weak_entry_t
结构体的实现,如下:
struct weak_entry_t {
DisguisedPtr<objc_object> referent; // 弱引用的对象
union {
// 弱引用数量大于 4 个用到的结构体
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;
};
// 弱引用数量不大于 4 个用到的结构体
struct {
// out_of_line_ness field is low bits of inline_referrers[1]
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; // 存储指向该对象的弱引用数组
};
};
// 判断是否用的是 referrers 来存储弱引用指针
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
结构体存放的是某个对象的所有弱引用指针,存放所有弱引用指针使用的是一个联合体,如果弱引用指针的数量不超过 4 个就用inline_referrers
存储,否则用referrers
存储。
结构图如下:
3.6 weak_referrer_t
weak_entry_t
用于存放所有指向某个对象的 weak
指针地址,类型是weak_entry_t
,实现如下:
typedef DisguisedPtr<objc_object *> weak_referrer_t;
3.7 结构图总结
4. 代码结合结构图具体分析
我们还从👆上面提到的storeWeak
方法中进行具体分析,代码如下,标注的一些点在下面会进行具体分析:
template <HaveOld haveOld, HaveNew haveNew,
CrashIfDeallocating crashIfDeallocating>
storeWeak(id *location, objc_object *newObj)
{
//校验旧对象和新对象必须存其一
ASSERT(haveOld || haveNew);
//校验如果haveNew=true,newObj不能为nil
if (!haveNew) ASSERT(newObj == nil);
Class previouslyInitializedClass = nil;
id oldObj;
SideTable *oldTable;
SideTable *newTable;
retry:
if (haveOld) {
//如果weak 指针指向旧值,就取出旧值
oldObj = *location;
//以旧对象地址为 key取出旧的SideTable
oldTable = &SideTables()[oldObj];
} else {
oldTable = nil;
}
if (haveNew) {
// 取出对应新的SideTable
newTable = &SideTables()[newObj];
} else {
newTable = nil;
}
//上锁
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
//校验,如果旧值对不上 goto retry
if (haveOld && *location != oldObj) {
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
goto retry;
}
//保证弱引用对象的isa非空,防止弱引用机制和+initialize 发生死锁
if (haveNew && newObj) {
Class cls = newObj->getIsa();
if (cls != previouslyInitializedClass &&
!((objc_class *)cls)->isInitialized())
{
SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
//如果class没有初始化发送+initialized消息
class_initialize(cls, (id)newObj);
previouslyInitializedClass = cls;
//到这里class肯定已经初始化了,在走一遍
goto retry;
}
}
if (haveOld) {
//<<1>>如果weak 指针之前指向了其他对象,在这里清空
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
if (haveNew) {
//通过newObj和location生成一个新的weak_entry_t并插入到newObj的弱引用数组中(weak_entries)
//<<2>>
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
// Set is-weakly-referenced bit in refcount table.
if (newObj && !newObj->isTaggedPointer()) {
//<<3>> 设置 isa 的标志位
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);
return (id)newObj;
}
4.1 解除 weak 指针之前旧的指向
方法调用如下:
// 参数 1:weak 指针指向对象所在的全局哈希表
// 参数 2:weak 指针指向的旧对象
// 参数 3:weak 指针的地址
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
weak_unregister_no_lock
方法主要是清除存储在weak_entry_t
中的weak_refrerrer_t
,如果删除后weak_entry_t
中的数组为空,则将整个weak_entry_t
从weak_table_t
中移除, 方法源码如下:
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_ptr的地址
weak_entry_t *entry;
if (!referent) return;
//<<1>> 查找referent对应的weak_entry_t
if ((entry = weak_entry_for_referent(weak_table, referent))) {
//<<2>>如果entry存在,删除entry中的referrer
remove_referrer(entry, referrer);
bool empty = true;
// 判断entry的动态数组referrers中是否有值
if (entry->out_of_line() && entry->num_refs != 0) {
empty = false;
}
else {
//判断entry的定长数组inline_referrers中是否有值
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i]) {
empty = false;
break;
}
}
}
//如果都是空的将entry从weak_table移除
if (empty) {
//<<3>>移除entry
weak_entry_remove(weak_table, entry);
}
}
4.1.1 查找referent对应的weak_entry_t
具体方法实现如下:
// weak_table 当前对象对应的SideTable中的弱引用表
// referent 当前对象
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
ASSERT(referent);
// 获取所有的weak_entry_t数组
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
// 通过对对象指针的哈希方法生成的值与 weak_table->mask 进行 BITMASK 操作得到一个起始值
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
return &weak_table->weak_entries[index];
}
- 每次遍历如果没有在
weak_entries
中找到referent
对应的weak_entry_t
,就对index + 1
再进行BITMASK
操作,遍历一次就记录一次,直到大于max_hash_displacement
最大偏移值,返回 nil,说明当前对象在weak_table
的weak_entries
中没有对应的weak_entry_t
,也就是说没有弱引用 - 如果某个
weak_entry_t
的referent
和参数referent
相等,说明找到了,返回这个weak_entry_t
4.1.2 删除 entry 中的 referrer
在上一步中我们找到了存储当前对象弱引用的weak_entry_t
,现在我们就需要从weak_entry_t
中的referrers
或者inline_referrers
中删除掉之前的弱引用,源码实现如下:
// entry: 当前对象对应的 weak_entry_t
// old_referrer : 弱引用指针的地址
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
if (! entry->out_of_line()) {
// 从 entry 的定长(长度为 4)数组inline_referrers中查找 old_referrer,如果有则置为 nil
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;
}
// 从动态数组中查找
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;
}
}
// 置为 nil,并且对应的数量-1
entry->referrers[index] = nil;
entry->num_refs--;
}
- 如果使用的是定长数组(数量为 4 个),那么就对
inline_referrers
进行遍历查找,如果存在弱引用指针old_referrer
,则设为 nil - 如果使用的是动态数组,那么就对
referrers
进行查找,如果存在,设为nil,并将引用数量-1,查找方法和上面👆那一步类似
4.1.3 如果 entry中的数组为空,则从 weak_table_t中删除 entry
源码实现如下:
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
// 如果使用的是动态数组,则释放动态数组的内存
if (entry->out_of_line()) free(entry->referrers);
// 以 entry 为起始地址的前sizeof(*entry)个字节区域清零
bzero(entry, sizeof(*entry));
// 全局 weak_table_t中的弱引用对象数量-1
weak_table->num_entries--;
// 收缩表大小
weak_compact_maybe(weak_table);
}
- weak_compact_maybe(weak_table) 收缩表大小
实现如下:
static void weak_compact_maybe(weak_table_t *weak_table)
{
// #define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
size_t old_size = TABLE_SIZE(weak_table);
// 如果 weak_table 的表大小占用超过 1024 个字节,并且 1/16比弱引用的对象的数量还多就收缩表的大小,使其不超过原来的 1/2
if (old_size >= 1024 && old_size / 16 >= weak_table->num_entries) {
weak_resize(weak_table, old_size / 8);
// leaves new table no more than 1/2 full
}
}
- weak_resize源码
static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
// 获取旧的大小
size_t old_size = TABLE_SIZE(weak_table);
weak_entry_t *old_entries = weak_table->weak_entries;
// 使用新的大小创建新的new_entries
weak_entry_t *new_entries = (weak_entry_t *)
calloc(new_size, sizeof(weak_entry_t));
// 将weak_table的成员变量重新赋值
weak_table->mask = new_size - 1;
weak_table->weak_entries = new_entries;
weak_table->max_hash_displacement = 0;
weak_table->num_entries = 0; // restored by weak_entry_insert below
// 如果old_entries还有值,则进行遍历重新放入 weak_table 新的weak_entries中
if (old_entries) {
weak_entry_t *entry;
weak_entry_t *end = old_entries + old_size;
for (entry = old_entries; entry < end; entry++) {
if (entry->referent) {
// 插入这一步在下面会讲到
weak_entry_insert(weak_table, entry);
}
}
// 释放旧的
free(old_entries);
}
}
4.2 生成新的 weak_entry_t 插入到weak_table_t中的 weak_entryies中
之前storeWeak
中相关的代码如下:
...
newObj = (objc_object *)
weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
crashIfDeallocating);
...
我们具体来看下weak_register_no_lock
方法的源码,实现如下:
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
// 如果为 nil,或者是TaggedPointer,则直接 return referent_id
if (!referent || referent->isTaggedPointer()) return referent_id;
// 判断当前对象是否正在释放或是否支持弱引用
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
@selector(allowsWeakReference));
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, @selector(allowsWeakReference));
}
if (deallocating) {
if (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;
}
}
weak_entry_t *entry;
// 判断如果对象已经在 weak_table 中存在弱引用记录,就在原来的 entry 上追加
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
// 如果不存在则创建一个新的 entry,加入到 weak_table中
else {
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
return referent_id;
}
4.2.1 weak_entry_t 中添加新的 weak_referrer_t
append_referrer
具体实现如下:
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
// 如果使用的是定长数组,找到为 nil 的位置,赋值即可
if (! entry->out_of_line()) {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return;
}
}
// 如果放不下了,就申请创建动态数组new_referrers
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
// 然后将之前定长数组的弱引用赋值给new_referrers
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[i];
}
// 设置 entry 相关的变量的值
entry->referrers = new_referrers;
entry->num_refs = WEAK_INLINE_COUNT;
entry->out_of_line_ness = REFERRERS_OUT_OF_LINE;
entry->mask = WEAK_INLINE_COUNT-1;
entry->max_hash_displacement = 0;
}
ASSERT(entry->out_of_line());
// 如果引用数量超过表大小的 3/4就自动扩容
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
// 在 referrers 中找到一个值为 nil 的 weak_referrer_t,用新的弱引用对其赋值,并将引用数量+1
size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (entry->referrers[index] != nil) {
hash_displacement++;
index = (index+1) & entry->mask;
if (index == begin) bad_weak_table(entry);
}
if (hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;
}
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}
- grow_refs_and_insert 对某个对象的弱引用表扩容并进行插入
__attribute__((noinline, used))
static void grow_refs_and_insert(weak_entry_t *entry,
objc_object **new_referrer)
{
ASSERT(entry->out_of_line());
// 获取旧表的大小
size_t old_size = TABLE_SIZE(entry);
// 如果之前有旧表,则扩容为之前的 2 倍,否则为 8
size_t new_size = old_size ? old_size * 2 : 8;
// 获取当前对象所有弱引用指针的数量
size_t num_refs = entry->num_refs;
// 获取旧的数组
weak_referrer_t *old_refs = entry->referrers;
// mask 赋值
entry->mask = new_size - 1;
// 使用新的 size 创建referrers,并给 entry 赋值
entry->referrers = (weak_referrer_t *)
calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));
// 刚开始都为 0
entry->num_refs = 0;
entry->max_hash_displacement = 0;
// 将老的弱引用指针地址放到新的里边
for (size_t i = 0; i < old_size && num_refs > 0; i++) {
if (old_refs[i] != nil) {
// 进行插入
append_referrer(entry, old_refs[i]);
num_refs--;
}
}
// 最后将新的弱引用指针地址进行插入
append_referrer(entry, new_referrer);
if (old_refs) free(old_refs);
}
4.2.2 new_entry(referent, referrer) 创建新的 entry
构造方法实现如下:
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;
}
}
从上面代码中可以看出,刚开始对象没有弱引用指针,使用的是定长数组inline_referrers
4.2.3 weak_grow_maybe weak_table扩容
实现代码如下:
static void weak_grow_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// 如果超过 3/4 则进行扩容,如果之前有,则为原来的 2 倍,否则为 64
if (weak_table->num_entries >= old_size * 3 / 4) {
weak_resize(weak_table, old_size ? old_size*2 : 64);
}
}
这里也是调用weak_resize
方法,将weak_table_t
进行扩容,上面👆是进行收缩。
4.2.4 weak_table_t 中插入 weak_entry_t
实现代码如下:
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
weak_entry_t *weak_entries = weak_table->weak_entries;
ASSERT(weak_entries != nil);
// 计算出要插入的位置
size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
size_t index = begin;
size_t hash_displacement = 0;
while (weak_entries[index].referent != nil) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_entries);
hash_displacement++;
}
// 进行赋值,数量自增
weak_entries[index] = *new_entry;
weak_table->num_entries++;
// 对最大max_hash_displacement偏移值进行赋值,这也是查找时遍历的临界点
if (hash_displacement > weak_table->max_hash_displacement) {
weak_table->max_hash_displacement = hash_displacement;
}
}
4.3 设置弱引用的标志位
这一步就是标记这个对象有弱引用,在这个对象 dealloc
的时候,将所有的弱引用释放掉,实现代码如下:
inline void
objc_object::setWeaklyReferenced_nolock()
{
retry:
// 获取对象的 isa 指针
isa_t oldisa = LoadExclusive(&isa.bits);
isa_t newisa = oldisa;
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
sidetable_setWeaklyReferenced_nolock();
return;
}
// 如果对象isa 的弱引用标志位已经是 true 了,则不进行操作
if (newisa.weakly_referenced) {
ClearExclusive(&isa.bits);
return;
}
// 弱引用标志位设为 true
newisa.weakly_referenced = true;
// //如果oldisa.bits和newisa.bits不相等返回NO,继续tery里面的内容,这时候newisa.weakly_referenced已经是true了,所以return
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
5. 弱引用指针的释放
- (void)dealloc {
_objc_rootDealloc(self);
}
⏬
void _objc_rootDealloc(id obj)
{
ASSERT(obj);
obj->rootDealloc();
}
⏬
inline void objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
⏬
id object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
⏬
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
⏬
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
⏬
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
终于来到了关键的方法weak_clear_no_lock
,实现如下:
*/
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
// 获取当前对象所在的weak_entry_t
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
// 遍历entry的 referrers 数组,将弱引用指针全置为 nil
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
// 从 weak_table中移除当前对象对应的 entry
weak_entry_remove(weak_table, entry);
}
上面流程主要是针对于weak
实现的介绍,其中对于 isa.nonpointer
还有 isa
的标志位以及 Tagged Pointer
没有进行过多介绍,之后文章会详细进行介绍。