#iOS 底层探究之 weak引用
关键字weak的使用在iOS开发中,可以说最常用之一。delegate、block、NSTimer中的循环引用问题,我们都需要使用weak关键词.下面我们来探讨下weak的底层逻辑是如何的。
weak的初始化
我们在使用weak时常常如下代码:
@property(nonatomic, weak) id<UITableViewDelegate> *delegate;
__weak NSObject *weakObj = obj;
如果只是从这样看weak,我们不知道其底层做了如何操作。我们可以在如下操作:
- 首先在 __weak 修饰变量那行加上断点:
- 然后开启汇编debug,这样我们看出底层做了什么操作,而且XCode在汇编debug会加上一些关键注释。注意: 需要关闭其他注释,不然会影响此次debug。
- 然后查看汇编debug, 看下面图片标记的1,该行汇编对应是__weak NSObject *weakObj = obj;,所以后续的2和3是runtime对__weak修饰的变量的操作。Runtime首先会调用objc_initWeak方法,在调用storeWeak方法。
- 然后我们在objc源码中在objc_initWeak()方法中打上断点,然后移除debug汇编,点控制台上的“continue program execution”,然后就跳转到objc_initWeak()方法体中。
- objc_initWeak方法通过传入一个location指针和obj对象来初始化,location指针指向__weak修饰的变量。然后调用storeWeak()方法.
/**
* Initialize a fresh weak pointer to some object location.
* It would be used for code like:
*
* (The nil case)
* __weak id weakPtr;
* (The non-nil case)
* NSObject *o = ...;
* __weak id weakPtr = o;
*
* This function IS NOT thread-safe with respect to concurrent
* modifications to the weak variable. (Concurrent weak clear is safe.)
*
* @param location Address of __weak ptr. weak指针
* @param newObj Object ptr. obj对象
*/
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
// location: weakObj, newObj: 源对象
return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
(location, (objc_object*)newObj);
}
- 从这就开始了weak指针的初始化。
storeWeak():Runtime弱引用对象逻辑
- 首先从SideTables中获取oldObj/newObj的weak table. oldObj即弱引用对象开始弱引用的目标对象,newObj当前弱引用的目标对象。
- 检查newObj对象的类型是否初始化,没有则先对其初始化,然后再从第一步重新开始
- 如果oldObj不为nil,则从oldObj的弱引用结构体中将location移除
- 如果newObj不为nil,则将location插入到newObj的弱引用结构体中 4.1 设置newObj的weakly_referenced=true,标记newObj被弱引用了,前提是newObj不是tagged Pointer或nil 4.2 *location = obj, 这样weakObj则和obj是同一个内存地址了,也没有对obj做retain操作。
- 调用newObj的_setWeaklyReferenced()方法,重置obj的weakly_referenced值。
enum CrashIfDeallocating {
DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};
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; // oldObj的weak table
SideTable *newTable; // newObj的weak table
// 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:
// 1. 获取oldObj的weak table
if (haveOld) { // 如果location原来有值,即weakObj从旧值指向新值
oldObj = *location;
oldTable = &SideTables()[oldObj]; // 获取oldObj的weak table
} else {
oldTable = nil;
}
// 2. 获取newObj的weak table
if (haveNew) { // 如果newObj有值
newTable = &SideTables()[newObj];
} else { // 将weakObj置为nil
newTable = nil;
}
SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable); // 加锁
if (haveOld && *location != oldObj) { // 如果有旧值,解锁oldObj的table和newObj的Table
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.
// 3. 判断newObj的类型是否初始化,没有则初始化,初始化后,在重新回到步骤1
if (haveNew && newObj) {
Class cls = newObj->getIsa(); // 获取源对象的isa
// 如果源对象的class没有初始化,则进入初始化
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.
// 4. 如果有旧值,需要从oldObj中移除weak指针
if (haveOld) {
weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
}
// Assign new value, if any.
// 5. 如果weakObj是弱引用一个新的Obj,则需要向newObj中注册
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.
// 5.1 如果newObj不是tagged pointer或nil,则设置weakly_referenced=true,标记newObj被弱应用了
if (!_objc_isTaggedPointerOrNil(newObj)) {
newObj->setWeaklyReferenced_nolock();
}
// Do not set *location anywhere else. That would introduce a race.
// 5.2weakObj指针指向newObj,即weakObj和newObj是同一个指针
*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.
// 6. 调用_setWeaklyReferenced(), 重置obj的weakly_referenced的值
callSetWeaklyReferenced((id)newObj);
return (id)newObj;
}
从oldObj中移除弱引用对象
- 调用weak_unregister_no_lock()方法从obj中移除一个弱引用对象
- 首先获取obj的弱引用结构体
- 从弱引用结构体中移除弱引用指针
- 如果弱引用结构体没有任何弱引用数据,则从weak table中移除该结构体
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 *entry;
if (!referent) return;
// 1. 首先获取obj的弱引用结构体
if ((entry = weak_entry_for_referent(weak_table, referent))) {
// 2. 从弱引用结构体中移除该弱引用
remove_referrer(entry, referrer);
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;
}
}
}
// 3. 如果弱引用结构体中没有弱引用数据,则将其从weak table中移除
if (empty) {
weak_entry_remove(weak_table, entry);
}
}
// Do not set *referrer = nil. objc_storeWeak() requires that the
// value not change.
}
- 弱引用结构体: 2.1 referent: 弱引用的目标对象,以它为key,存储在weak table中 2.2 out_of_line_ness 用来计算是否即将存满,一般是集合的3/4 2.3 num_refs已存储的弱引用个数,和out_of_line_ness用来判断弱引用集合是否即存放满 2.4 inline_referrers保存弱引用指针的集合 2.5 weak_referrer_t: 保存弱应用地址,该指针是伪装存储的,所以内存分析工具不会看到很多从弱表到对象的内部指针。
#define REFERRERS_OUT_OF_LINE 2
struct weak_entry_t {
DisguisedPtr<objc_object> referent; // 弱引用的目标对象
union {
struct {
weak_referrer_t *referrers; // 弱引用结合
uintptr_t out_of_line_ness : 2; // 用来计算是否即将存满,一般是集合的3/4
uintptr_t num_refs : PTR_MINUS_2; // 已存储的弱引用个数
uintptr_t mask; // hash计算时的求余的被除数
uintptr_t max_hash_displacement; // hash冲突时的移动数,为0,
};
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是否相等
weak_entry_t& operator=(const weak_entry_t& other) {
memcpy(this, &other, sizeof(other));
return *this;
}
/// 初始化weak_entry_t
/// @param newReferent 指向目标对象的指针
/// @param newReferrer 指向弱引用指针的指针
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;
}
}
};
3.弱应用指针:
// 弱引用变量的地址。
// 这些指针是伪装存储的,所以内存分析工具不会看到很多从弱表到对象的内部指针。
typedef DisguisedPtr<objc_object *> weak_referrer_t;
4.获取obj的弱引用结构体, 首先获取weak table下弱引用结构体集合,然后遍历该集合,如果弱引用结构体的目标对象和当前目标对象是否相等,如果相等则退出循环,并范湖该结构体。
/// 从weak table中获取obj的弱引用结构体
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
ASSERT(referent);
// 获取weak table下所有的弱引用结构体集合
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
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;
}
}
// 返回obj的弱引用结构体
return &weak_table->weak_entries[index];
}
5.从obj的弱引用结构体中移除弱引用指针:遍历弱引用集合(链表),查找到弱引用指针存储的位置,然后将对应位置的元素设置为nil(注意: 不是弱引用对象设置为nil),并设置存储弱引用个数-1。 5.1 弱引用集合的元素指向弱引用指针,弱引用指针指向目标对象
/// 移除目标对象的弱引用对象
static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
// 判断是否存满
if (! entry->out_of_line()) {
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
// 找到弱引用指针元素,将该元素置为nil
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,注意不是将弱引用置为nil
entry->referrers[index] = nil;
entry->num_refs--; // 弱引用个数-1
}
6.如果弱引用集合为空,则将弱引用结构体从weak table中移除。
/// 从weak table中移除obj的弱引用数据
static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
// remove entry
// 释放弱引用集合
if (entry->out_of_line()) free(entry->referrers);
bzero(entry, sizeof(*entry));
// weak table中的弱应用对象个数-1
weak_table->num_entries--;
// 检查weak table内存空间,如果大部份为nil,则分配新空间存储当前值,释放原有空间
weak_compact_maybe(weak_table);
}
7.如果weak table存储的元素个数大于1024,或者小于其集合的1/16,则重新分配内存空间.
// Shrink the table if it is mostly empty.
// 如果weak table大部份元素为nil,则重新分配内存空间
static void weak_compact_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// Shrink if larger than 1024 buckets and at most 1/16 full.
// 若存储元素个数大于1024其存储的数据小于1/16则分配新的空间
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 table分配新的内存空间
/// 为weak table分配新的内存空间
static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
size_t old_size = TABLE_SIZE(weak_table);
// 获取weak table中的弱引用存储的旧数据
weak_entry_t *old_entries = weak_table->weak_entries;
// 为weak table中存储的弱引用数据开辟新的内存空间
weak_entry_t *new_entries = (weak_entry_t *)
calloc(new_size, sizeof(weak_entry_t));
weak_table->mask = new_size - 1; // 弱引用表大小
weak_table->weak_entries = new_entries; // 弱引用链表
weak_table->max_hash_displacement = 0; // 最大hash值移动
weak_table->num_entries = 0; // restored by weak_entry_insert below, 恢复原来的数据
if (old_entries) {
weak_entry_t *entry;
weak_entry_t *end = old_entries + old_size; // 获取weak table中最后一个元素
// 将原来的weak table中的数据存储到新的内存中
for (entry = old_entries; entry < end; entry++) {
if (entry->referent) {
weak_entry_insert(weak_table, entry);
}
}
free(old_entries); // 释放原来的weak table数据
}
}
向obj插入弱引用对象
1.通过weak_register_no_lock()向obj插入弱引用对象,首先获取obj的弱引用结构体,如果存在则直接插入弱引用指针;否则新建一个弱引用结构体,在插入到weak table中。
/**
* Registers a new (object, weak pointer) pair. Creates a new weak
* object entry if it does not exist.
*
* @param weak_table The global weak table.
* @param referent The object pointed to by the weak reference. // 被弱引用的对象
* @param referrer The weak pointer address. // 弱引用对象
*/
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 (_objc_isTaggedPointerOrNil(referent)) 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;
// 获取目标对象的弱引用数构体
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer); // 向目标对象的弱引用结构体中插入一个弱引用指针
}
else { // 如果没有目标对象的弱引用结构体,则新建一个结构体
// 使用目标对象和弱引用指针新建一个目标对象的弱引用结构体
weak_entry_t new_entry(referent, referrer);
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry); // 向weak table中插入一个元素(目标对象的弱引用结构体)
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
- 新建弱引用结构体对象:向obj第一次插入弱引用对象时,弱引用结构体是空的,所以需要新建一个弱应用结构体对象。
weak_entry_t new_entry(referent, referrer);
- 其次判断obj所在的weak table是否已经存满, 如果已经存储3/4的控件,则扩容weak table的内存空间,扩容大小为原来的2倍。
// Grow the given zone's table of weak references if it is full.
static void weak_grow_maybe(weak_table_t *weak_table)
{
size_t old_size = TABLE_SIZE(weak_table);
// Grow if at least 3/4 full.
// 对weak table扩容
if (weak_table->num_entries >= old_size * 3 / 4) {
weak_resize(weak_table, old_size ? old_size*2 : 64);
}
}
- 向weak table中插入弱引用结构体:
/// 向weak table中插入弱引用结构体
static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
// 获取weak table的弱应用结构体集合
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;
// 获取首个不为nil的元素位置
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++; // 更新存储元素个数
if (hash_displacement > weak_table->max_hash_displacement) {
weak_table->max_hash_displacement = hash_displacement;
}
}
- 当obj有弱引用结构体对象时,直接插入弱引用对象: 5.1 如果弱引用结构体的弱引用集合没有存满,则直接存入 5.2 如果弱引用结合即将存满(即达到或超过3/4),则进行2倍扩容,在存入弱引用对象
/// 向弱引用结构体中插入一个弱引用指针
static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
if (! entry->out_of_line()) { // 如果弱引用结构体的弱引用集合没有存满
// Try to insert inline.
// 将弱引用指针插入到集合中,找到第一个指向为nil的元素,将该元素指向弱引用指针
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
if (entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return;
}
}
// Couldn't insert inline. Allocate out of line.
// 如果不能线性插入,则创建线性内存空间
// 一般不会执行这里
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
// This constructed table is invalid, but grow_refs_and_insert
// will fix it and rehash it.
// 将原元素插入到新的内存中
for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
new_referrers[i] = entry->inline_referrers[i];
}
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; // hash计算的mask值
entry->max_hash_displacement = 0; // hash冲突时移动的大小
}
ASSERT(entry->out_of_line());
// 如果超过弱引用集合的3/4, 则进行2倍扩容
if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
return grow_refs_and_insert(entry, new_referrer);
}
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++; // 弱引用结构体已保存弱引用个数
}
- 如果超过弱引用集合的3/4, 则进行2倍扩容
/// 为弱引用集合扩容
__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);
// 设置新的弱引用集合大小为原来的两倍,如果原弱引用集合大小为负数时,大小为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; // 获取原弱引用集合
entry->mask = new_size - 1; // 设置弱引用结构体的hash mask
// 为新弱引用结合分配空间
entry->referrers = (weak_referrer_t *)
calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));
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--; // 原来存放的弱引用个数,计算还有多少个未插入多新的弱引用集合中
}
}
// Insert
// 设置弱引用结构体的弱引用集合
append_referrer(entry, new_referrer);
if (old_refs) free(old_refs); // 释放旧弱引用集合
}
Obj释放时,weakObj被置为nil
我们使用weak来打破循环引用的重要原因之一,是当obj被释放后,weakObj会自动置为nil,我们来查看下weakObj是如何置为nil的。 查看如下代码:
NSObject *obj = [NSObject new];
__weak NSObject *weakObj = obj;
obj = nil;
- 在obj 设置为nil前,会先对obj进行释放操作逻辑,会调用NSObject的 -dealloc方法,然后再对weak引用释放。
- deallock 经过多层调用最后会objc_object::clearDeallocating_slow()方法中判断obj是否有弱引用(isa.weakly_referenced == true),如果为真,则调用 weak_clear_no_lock()方法释放weak引用,并置为nil.
- weak_clear_no_lock() 逻辑:
- 1.获取弱引用结构体
- 2.获取弱引用链表
- 3.将弱引用链表节点指向的弱引用指针置为nil,这样节点指向的内容也是nil
- 4.从weak table中移除obj的弱引用结构体
// 清除weak指针,并将weakObj置为nil
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
// 获取弱引用结构体
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;
}
// 遍历弱引用链表,即弱引用指针置为nil,这样节点指向的内存也是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中移除
weak_entry_remove(weak_table, entry);
}
- 这样完成了弱引用置为nil的逻辑。
总结
- 调用objc_initWeak()初始化指向弱引用对象的指针
- 调用storeWeak()将弱引用指针与obj关联起来。
- 弱引用指针是否有旧值
- 获取oldObj目标对象在weak table中的弱引用结构体
- 从旧目标对象的弱引用结构体对象中移除弱引用指针 2.1 将弱引用结构体的弱引用集合指向该弱引用指针的元素置为nil 2.2 如果弱引用结构体中的弱引用集合为空,则将弱引用结构体从weak table中移除
- 将弱引用指针和newObj关联起来
- 获取newObj目标对象在weak table中的弱引用结构体
- 如果弱引用结构体为空,则使用目标对象和弱引用指针新建一个弱引用结构体,并将该结构体插入到weak table中
- 如果弱引用结构体不为空,则直接将弱引用指针插入到该弱引用结构体中
- 如果弱引用结构体已经使用的空间超过3/4, 先对其进行扩容,扩容规则为原来的两倍,在插入新的弱引用对象
- 如果obj被释放时,会在释放前,将weak引用都置为nil 1.获取弱引用结构体 2.获取弱引用链表 3.将弱引用链表节点指向的弱引用指针置为nil,这样节点指向的内容也是nil 4.从weak table中移除obj的弱引用结构体
- weak在Runtime中的逻辑流程图:
- weak指针的内存结构图: