runtime之weak指针的实现

我们经常写delegate ,修饰有weak指针,而不用assign,这是因为用weak指针不会,在delegate对象释放的时候不会引起崩溃,而assign会引起崩溃。(野指针)。这里就有个疑问,为什么用weak不会引起崩溃呢?

我的源码地址

weak指针存放地址

我们查看源码文件NSObject.mm文件,我们看见有许多带有weak的api。

id
objc_initWeak(id *location, id newObj)
id
objc_storeWeakOrNil(id *location, id newObj)
id
objc_storeWeak(id *location, id newObj)
static id 
storeWeak(id *location, objc_object *newObj)
id
objc_initWeakOrNil(id *location, id newObj)
void
objc_destroyWeak(id *location)
id
objc_loadWeakRetained(id *location)
id
objc_loadWeak(id *location)
void
objc_copyWeak(id *dst, id *src)
void
objc_moveWeak(id *dst, id *src)

其实这些api就是用来操作weak指针的
从这些api中我们我们首先要找初始化函数
从apple对每个函数的注释上我们能看出来,程序在启动点时候调用初始化函数id
objc_initWeak(id location, id newObj)。
我在空白程序中加入
objc_initWeak* 信号断点,运行日志如下。

image.png

我们能看出来,程序在启动的时候在main函数之后(在main出打的断点,先执行在执行objc_initWeak)初始化的该函数。

函数入口objc_initWeak

找到函数入口了,那我们就需要借助源码往下看,看看objc_initWeak 函数初始化到底干了啥事情

/** 
 * 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. 
 * @param newObj Object ptr. 
 */
id
objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<false/*old*/, true/*new*/, true/*crash*/>
        (location, (objc_object*)newObj);
}

我认为有必要把apple的注释也贴出来,供英文好的人看看么

1 判断newObj 是不是空,空就返回
2 调用storeWeak 函数 。

看这里关键是函数storeWeak 的调用了

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:
    /// oldObj 是若引用指针
    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(_class_getNonMetaClass(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_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()) {
            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;
}

参数:HaveOld = false ,HaveNew =true,CrashIfDeallocating=true, 还有两个外界传入的location和newObj。五个

准备知识

1我们在上面的函数中有这么段代码 oldTable = &SideTables()[oldObj]; 这是c++ 的写法,我们要搞懂这是在干么才能理解这段代码。这段代码调用了 下面函数

static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

reinterpret_cast 是强制类型转换,将SideTableBuf 转换成StripedMap<SideTable>类型。
SideTableBuf 是什么呢?

// We cannot use a C++ static initializer to initialize SideTables because
// libc calls us before our C++ initializers run. We also don't want a global 
// pointer to this struct because of the extra indirection.
// Do it the hard way.
alignas(StripedMap<SideTable>) static uint8_t SideTableBuf[sizeof(StripedMap<SideTable>)];

我们搞明白c++ 中的StripedMap<SideTable>。我们知道是StripedMap 是类,< SideTable> 给StripedMap 类传入的模板,我们看 StripedMap 有好多T 其实T 在这里应该换成SideTable 。
再回来看我们就知道其实就是分配了一个StripedMap 对象大小的内存而已。内存大小和StripedMap一样,那么我们就可以把这块内存当做StripedMap对象使用了。
&SideTables()[oldObj] ,其实就是StripedMap 方法调用

 T& operator[] (const void *p) { 
       return array[indexForPointer(p)].value; 
   }
 static unsigned int indexForPointer(const void *p) {
       uintptr_t addr = reinterpret_cast<uintptr_t>(p);
       return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
   }

这里我们能看出来,我们调用static unsigned int indexForPointer(const void *p) 函数,这个函数首先获取p的指针,对addr进行操作符操作,再进行取余操作,获取的值命名为index。(这里其实就是将p经过一定的运算,获取一个大小在0~64 之间的值)
我们再从Array中获取下标值index。这里其实就是获取到一个结构体对象SideTable。

见图


image.png
  • 红色代表类名
  • 粉红代表类中的项
  • 黄色代表的数组中的item

调用顺序

1 检测参数,HaveOld 和 HaveNew 不能同时是false
2 HaveNew = false ,那么newObj 必须是nil。
3 声明几个变量,Class previouslyInitializedClass , id oldObj; SideTable oldTable; SideTable newTable;
4 这里要是 HaveOld = YES ,从location的地方获取对象赋值给oldObj,获取oldTable 表地址
要是HaveNew = YES, 获取newObj 处 的SideTable 表地址
5 根据HaveOld 和 HaveNew 值分别锁定对应的SideTable 表
6 如果HaveOld = yes,并且oldObj 不在位置
location 所在地址,那么就重新执行4。这里保证oldObj 一定在
location 所在位置。别的执行7
7.如果HaveNew 并且还有newObj 对象,执行8操作,否则执行9
8获取下newObj 对象isa,如果class没有实例化,那么实例化下。返回到4重新执行。别的执行9
9 如果HaveOld = yes, 那么调用void
weak_unregister_no_lock(weak_table_t weak_table, id referent_id,
id referrer_id)函数。(
具体分析这个函数在后面**),将对象从 oldTable 表中删除
10 如果 HaveNew = yes,调用id
weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating) ,将值写入newTable中,并且检查newObj是不是isTaggedPointer,设置newObj的值标记weak位置。并且把location 存入值newObj。
11 解锁 表返回

上面两个函数比较重要我们看看对象是如何从SideTable 存入和删除的。我们先看看SideTable 表结构,在看如何删除和存入的

siderTable结构比较简单

SideTable.png

1 有三个变量,spinlock_t slock; 锁,weak_table_t weak_table;weak指针表
weak_table_t 结构体,四个成员,这里主要看 weak_entry_t *weak_entries;
weak_entry_t 结构如图

#define WEAK_INLINE_COUNT 4
struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            weak_referrer_t *referrers;
            uintptr_t        out_of_line : 1;
            uintptr_t        num_refs : PTR_MINUS_1;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line=0 is LSB of one of these (don't care which)
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
};

我们看看weak对象如何存入上述结构的

/** 
 * 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, bool crashIfDeallocating)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {
        deallocating = referent->rootIsDeallocating();
    }
    else {
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_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;
        }
    }

    // now remember it and where it is being stored
    weak_entry_t *entry;
    ///真实地址 全局map
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        append_referrer(entry, referrer);
    } 
    else {
        weak_entry_t new_entry;
        new_entry.referent = referent;
        new_entry.out_of_line = 0;
        new_entry.inline_referrers[0] = referrer;
        for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) {
            new_entry.inline_referrers[i] = nil;
        }
        
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }

    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.

    return referent_id;
}

  • 参数: weak_table_t 需要存入对象表,referent_id 需要存入对象指针(weak指针指向的真实对象地址),referent_id 引用的地址(weak对象指针地址),crashIfDeallocating 是否打印日志。
  • 调用顺序
  1. 获取真是对象地址,获取weak对象指针地址。
    2.如果真实对象是nil,或者是tag指针,返回引用的地址(因为TaggedPoint 同样的值就一个地址)
    image.png

    image.png

    3判断真实对象是否释放了,没有被释放,检测下是否允许weak指针引用。(这里不到存入数据,可以不仔细看)
    4 声明变量 weak_entry_t entry;
    5 调用 weak_entry_for_referent 函数 (
    这个函数是在weak_table_t 表中查找真是对象对应的weak_entry_t,没找到返回nil,找到返回weak_entry_t ),返回不是nil,执行6 ,返回nil 执行7
    6 . 查询结果不是nil,调用append_referrer(
    我们将weak指针写入 真实对象对应的weak_entry_t 结构体中

    7 .查询结果是nil ,我们我们创建一个新的weak_entry_t。(
    这里我们知道了weak_entry_t 结构体的成员变量的含义了,referent 指向weak对象,inline_referrers[0] 指向对象地址
    8 调用weak_grow_maybe 查询并且扩展表(
    查询表大小是否够了,不够需要扩展表
    9 调用weak_entry_insert 将 entry 存入表中。(
    数据存入表中*)

我们看看上面步骤中的5步具体调用

static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    assert(referent);

    weak_entry_t *weak_entries = weak_table->weak_entries;

    if (!weak_entries) return nil;
    ///位置  散列表
    size_t index = hash_pointer(referent) & weak_table->mask;
    size_t hash_displacement = 0;
    ///判断相等不相等查找过程
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    
    return &weak_table->weak_entries[index];
}
  1. 要是weak_table_t 中的weak_entries 变量是nil ,返回nil(没有创建weak_entry_t 结构体)
    2.根据真实对象指针referent 与weak_table_t 的mask 获取一个位置index。
    3.查询 从index 的位置开始循环查询weak_entry_t 对象是否包含真实对象的指针,要是所有的的weak_entry_t 都不包含,返回nil(说明还没有弱指针指向这个对象)。有就返回这个weak_entry_t 结构体。

接着我们看看查询到weak_entry_t 调用append_referrer 如何存入弱指针的

/** 
 * Add the given referrer to set of weak pointers in this entry.
 * Does not perform duplicate checking (b/c weak pointers are never
 * added to a set twice). 
 *
 * @param entry The entry holding the set of weak pointers. 
 * @param new_referrer The new weak pointer to be added.
 */

static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    if (! entry->out_of_line) {
        // Try to insert inline.
        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 = 1;
        entry->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
    }
///一定大于1
    assert(entry->out_of_line);
// 4  >=3
    if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        return grow_refs_and_insert(entry, new_referrer);
    }
    
    ///
    size_t index = w_hash_pointer(new_referrer) & (entry->mask);
    size_t hash_displacement = 0;
    while (entry->referrers[index] != NULL) {
        index = (index+1) & entry->mask;
        hash_displacement++;
    }
    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++;
}

看这个函数,我们要明白下weak_entry_t 结构体每个成员变量的作用才行,这里分析源码获取的。

#define WEAK_INLINE_COUNT 4
struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            weak_referrer_t *referrers;
            uintptr_t        out_of_line : 1;
            uintptr_t        num_refs : PTR_MINUS_1;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line=0 is LSB of one of these (don't care which)
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
};
  • DisguisedPtr<objc_object> referent; 真实对象地址,相当于key
  • weak_referrer_t *referrers; 当弱引用对象多余4个的时候,存入弱引用地址
  • uintptr_t out_of_line : 1; 0 代表 我们使用weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; 存入若引用地址,1 代表我们是用weak_referrer_t *referrers; 指针存入地址。 当弱引用的数量大于4 改指针变成1
  • uintptr_t num_refs : PTR_MINUS_1; 记录当前集合存入了多少个弱引用指针
  • uintptr_t mask; 代表最多可以存入多少个若引用指针。
  • uintptr_t max_hash_displacement; 从index位置偏移的位置。
  • weak_referrer_t inline_referrers[WEAK_INLINE_COUNT]; 当若引用指针少于四个存入在该变量中

append_referrer 调用分析

1 首先判断weak_entry_t 的out_of_line 变量是否是0,不是0 执行4,是0 执行2.
2 检查 weak_entry_t 的inline_referrers 指针是否存入四个值,没有则将对这个真实对象的weak指针new_referrer存入到inline_referrers 中。否则执行3
3 要是weak_entry_t 结构体的inline_referrers指针存满了,那么我们重新分配空间new_referrers,类型是weak_referrer_t,将inline_referrers 数据存入到new_referrers 指针对应的地址,然后将weak_entry_t 结构体的referrers指向new_referrers ,同时,将weak_entry_t 结构体的num_refs 赋值为WEAK_INLINE_COUNT(宏定义,数值4),将out_of_line 更新为1 (说明指针存在结构体weak_entry_t的referrers中),weak_entry_t 的mask 是WEAK_INLINE_COUNT -1.(因为下标是0开始的),这时候还没有便宜,weak_entry_t的max_hash_displacement = 0;
4 到这里说明对该对象的weak指针已经多于4个了。判断要是已经存入的指针是总体指针的75%。说明存储weak指针的空间不足,需要重新分配内存,这里调用grow_refs_and_insert方法实现。要是空间充足,那么调用 5
5 .获取new_referrer 的在 weak_entry_t mask范围内的index,查找该位置是否已经被存入值了。存入了值,就index +1 ,hash_displacement 累加1,继续执行5 。直到找到空的位置为止。
6 将hash_displacement 写入到weak_entry_t 结构体的max_hash_displacement
7 将数据存入想应的index,让num_refs 计数加1。

我们接着看看第四步调用的grow_refs_and_insert 方法,扩展weak指针空间

__attribute__((noinline, used))
static void grow_refs_and_insert(weak_entry_t *entry, 
                                 objc_object **new_referrer)
{
    assert(entry->out_of_line);
///4
    size_t old_size = TABLE_SIZE(entry);
    ///8
    size_t new_size = old_size ? old_size * 2 : 8;
/// 4
    size_t num_refs = entry->num_refs;
    /// 4
    weak_referrer_t *old_refs = entry->referrers;
    ///7
    entry->mask = new_size - 1;
    /// 8
    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);
}

调用逻辑分析

1.获取weak_entry_t 结构体的mask 数量,赋值给old_size
2.获取new_size的大小,是old_size大小的两倍。
3.获取 weak_entry_t 结构体的num_refs ,意思是当前存入的指针数量,赋值给num_refs

  1. 获取weak_entry_t 结构体 referrers ,保存在old_refs 中,
  2. 重新写入weak_entry_t 的mask大小,值是new_size-1.(比原来扩大两倍了)
    6.给weak_entry_t 的referrers 重新分配空间,数量是new_size 大小个weak_referrer_t空间
    7 因为新分配空间,设置weak_entry_t结构体的num_refs 是0,weak_entry_t结构体的max_hash_displacement 是0
    8.调用append_referrer 将老的指针写入到weak_entry_t结构体的referrers 中
    9 将新的new_referrer 写入到weak_entry_t结构体的referrers 中

回到函数weak_register_no_lock中,我们分析下当如果在weak_table_t 表中没有找到weak_entry_t结构体的时候,我们调用的函数

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.
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}

1 获取weak_table_t 的mask
2 查看weak_table_t 的空间大小如果不足75%.那么扩展空间两倍。实现和weak_entry_t 扩展对象空间一样。不做详细讲解了。

看到这里我想大概weak指针如何存入的应该明白了。我们绘制下weak指针的存入结构。
见图


image.png

存入顺序是
1.根据对象obj获取stripedMap 中的 所对应的SideTable 结构体
2.根据对象obj 获取SideTable 中weak_table_t 结构体下 对应的weak_entry_t结构体,所有关于obj对象的弱指针都存放在该weak_entry_t结构体里面。
3.将weak指针存入到weak_entry_t 下的weak_referrer_t 中

接下来我们看看如何获取weak指针

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;

    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        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;
                }
            }
        }

        if (empty) {
            weak_entry_remove(weak_table, entry);
        }
    }

    // Do not set *referrer = nil. objc_storeWeak() requires that the 
    // value not change.
}

调用顺序如下

  1. referent指向真是对象
    2.referrer 指向weak指针对象
    3 如果referent 是nil 就返回
    4 从weak_table_t 表中根据真实对象(referent)获取到对应的weak_entry_t 结构体,没有就直接结束了。
  2. 找到了真是对象对应的weak_entry_t结构体,那么调用remove_referrer, 删除 weak指针。(具体怎么删除下面分析)。
    6这里判断weak_entry_t 的out_of_line =1 并且weak_entry_t的num_refs不是0,说明还有weak指针。不要删除这个真实对象对应的weak_entry_t结构体
    7要是weak_entry_t 的out_of_line=0 ,我们知道对象存入在weak_entry_t 的inline_referrers中,检查weak_entry_t 的inline_referrers 的是否是nil,如果是空那么就删除掉真实对象(referent)对应的weak_entry_t结构体

看到这里其实就是我们存入的逆顺序删除而已。
上面的函数只是真正的删除了weak_entry_t 结构体而没有对weak指针删除,这里我们再看看真正weak指针的删除。

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++) {
            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 index = w_hash_pointer(old_referrer) & (entry->mask);
    size_t hash_displacement = 0;
    while (entry->referrers[index] != old_referrer) {
        index = (index+1) & entry->mask;
        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;
        }
    }
    entry->referrers[index] = nil;
    entry->num_refs--;
}
  1. 判断weak_entry_t结构体 的out_of_line = 0 ,那么我们就判断weak_entry_t 结构体的inline_referrers中是否存在weak指针,存入将该区域设置为nil
  2. 要是weak_entry_t结构体 的out_of_line = 1 ,那么我们获取下weak指针对应的在 weak_referrer_t *referrers 的index位置。
    3.查询index 处的weak指针是否和删除的指针相等,不相等index累加,hash_displacement累加,检查hash_displacement 是否已经超过weak_entry_t 结构体的max_hash_displacement的值,超过就结束,没有那么继续执行3.
    4.查询到 需要删除对象的位置,将该位置设置nil
    5 将weak_entry_t 结构体的num_refs 减去1.(删除掉一个指针了)

我们这里已经把weak指针的存入和删除都分析完毕了。

这里还有一点没说明,就是当真实对象释放掉了。如何清理对象所在的weak指针。
当对象释放掉的时候,会调用到对象的dealloc方法。dealloc方法中会调用到这个方法.void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 将对象所对应的weak表清除掉

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;
    }
    
    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_entry_remove(weak_table, entry);
}

这个函数实现很简单,不做介绍了。

纯手工自己摸索写的,哪里不对请给个指正。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容