美文网首页
弱指针weak的实现学习

弱指针weak的实现学习

作者: 我才是臭吉吉 | 来源:发表于2019-08-13 00:14 被阅读0次

1. weak对象的实现函数

首先,看个例子(取自《Objective-C高级编程 iOS与OS X多线程和内存管理》的第一部分):

id __weak weakObj = [[NSObject alloc] init];

其模拟代码为

id obj;

// 创建临时对象tmp
id tmp = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(tmp, @selector(init));

// 使用tmp创建weak对象obj
objc_initWeak(&obj, tmp);

// 作用域结束,tmp释放
objc_release(tmp);

// 释放weak对象obj
objc_destroyWeak(&obj);

通过代码可以看到,weak指针(weak对象)的创建及释放,实际上是通过objc_initWeak()objc_destroyWeak() 函数实现的**。现在,就需要查看一下iOS的运行时系统是如何实现weak的功能的。

注:源代码采用objc.723版本

objc_initWeak的实现:

id
objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

objc_destroyWeak的实现:

void
objc_destroyWeak(id *location)
{
    (void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
        (location, nil);
}

补充一下,修改weak对象的函数objc_storeWeak的实现:

id
objc_storeWeak(id *location, id newObj)
{
    return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object *)newObj);
}

可以看出,weak的相关功能,都是通过storeWeak函数进行实现的。通过不同的参数及条件设置实现weak对象的创建、修改和删除的**。

2. storeWeak的实现

还是不废话,直接贴出实现源码(删除了部分无关代码):

/**
 更新weak变量。
 
 HaveOld为true时,即weak变量本身已经指向一个即将被清空的对象。这个值可以是nil。
 HaveNew为true时,即有个新对象需要赋值给weak指针。这个值可以为nil。
 CrashIfDeallocating为true时,如果newObj正在释放、或者newObj所属的类不支持弱引用,则进程就要中断(崩溃);如果CrashIfDeallocating为false,则意味着将nil存储到变量中。
 
 @param location __weak指针地址
 @param newObj  真正指向的对象
 */
template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>
static id 
storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew); // 至少一个为true
    if (!haveNew) assert(newObj == nil); // 无新值时,newObj必须为nil

    id oldObj;
    // SideTable即为内存管理的数据结构。内部包含:引用计数表、弱引用表、自旋锁及相关方法。
    SideTable *oldTable; // 旧表(SideTable结构体指针)
    SideTable *newTable; // 新表(SideTable结构体指针)

    // Acquire locks for old and new values.
    // Order by lock address to prevent lock ordering problems. 

    if (haveOld) {
        // 若存在旧值
        
        // 从weak地址中取出旧对象
        oldObj = *location;
        // 使用旧对象获取到旧表实例
        oldTable = &SideTables()[oldObj];
    } else {
        // 若不存在旧值
        
        // 旧表直接置为nil
        oldTable = nil;
    }
    
    
    if (haveNew) {
        // 若存在新值
        
        // 使用新值初始化得到新表实例
        newTable = &SideTables()[newObj];
    } else {
        // 若不存在新值
        
        // 新表直接置为nil
        newTable = nil;
    }

    // 根据haveOld和haveNew状态,给两张SideTable表进行对应加锁(自旋锁)
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    ...

    // Clean up old value, if any.
    if (haveOld) {
        // 若存在旧值
        
        // 在旧表中的weak表中清除相关信息(旧对象和weak指针的关联)
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // Assign new value, if any.
    if (haveNew) {
        // 若存在新值
        
        // 在新表的weak表中存储相关信息(使用newObj的地址作为key,location地址,即weak指针作为value)
        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
        // 如果被打断(如此时正在释放对象),则newObj就为nil了【注:crash的已经在上一步中进行处理了,这里是不crash的情况】

        // Set is-weakly-referenced bit in refcount table.
        if (newObj  &&  !newObj->isTaggedPointer()) {
            // 若已经绑定成功,且newObj不是taggedPointer
            
            // 将newObj的对应位标记为weak
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        // 确保weak指针指向newObj
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
        // 若没有新值,原存储信息不变
    }
    
    // 解锁相关表
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    // 返回newObj对象
    return (id)newObj;
}

在以上代码中可以看到,我们所有的操作(注册weak、移除weak等)都是发生在SideTable实例中的。且在操作时需要在有锁的条件下执行。我们看一下SideTable到底是何方神圣:

struct SideTable {
    /** 自旋锁 */
    spinlock_t slock;
    /** 引用计数表(散列表) */
    RefcountMap refcnts;
    /** weak表(内部使用数组或二级表实现) */
    weak_table_t weak_table;
    
    ...
}

可以看到,SideTable即为内存管理的精髓。其内部包含的就是引用计数表和weak弱引用表

SideTable是全局对象(不支持析构,会crash),是由分离锁进行管理的StripedMap对象:

// 获取oldTable对象(根据oldObj对象)
oldTable = &SideTables()[oldObj];

其中,SideTables函数的实现为:

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

在StripedMap模板类中,我们可以找到获取SideTable的相关实现:

template<typename T>
class StripedMap {

    enum { CacheLineSize = 64 };

#if TARGET_OS_EMBEDDED
    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<uintptr_t>(p);
        
        // hash算法(64位系统下得到的是0~63)
        return ((addr >> 4) ^ (addr >> 9)) % Stripe Count;
    }

 public:
    T& operator[] (const void *p) { 
        return array[indexForPointer(p)].value; 
    }
    
    ...
}

其中,public方法中的实现即说明了一切:通过对象地址得到的hash值,取出数组中保存的值(这里即为SideTable对象)。且通过hash算法还可知道,一个StripedMap对象在64位系统下包含64个子单元。可以理解为一个分离锁管理着64个对象的SideTable对象。

由于本篇学习的是weak的实现,故我们暂时只讨论weak_table_t这个结构(这个还凑合,引用计数还根本不懂...)。

3. weak弱引用表

首先看一下weak_table_t的数据结构:

/**
 * 全局弱引用表。
 * weak_entry_t内部使用原始对象作为key,
 * weak指针的地址的数组作为value
 */
struct weak_table_t {
    /** 弱引用键值数据数组的起始地址 */
    weak_entry_t *weak_entries;
    /** weak_entry_t数组的元素个数 */
    size_t    num_entries;
    /** weak_entry_t数组的大小 */
    uintptr_t mask;
    /** hash查找最大偏移量 */
    uintptr_t max_hash_displacement;
};

weak_table_t内部保存着由week_entry_t实例组成的数组

内部的weak_entry_t即为真正的键值对:key为引用的对象,value为weak指针组成的数组

// 定义的结构体内部对于weak指针个数的储存上线
#define WEAK_INLINE_COUNT 4

// out_of_line_ness成员占用了inline_referrers数组index为1空间中的低2位。
// inline_referrers[1]是一个指针对齐的DisguisedPtr的对象。
// 一个指针对齐的DisguisedPtr的最低两位永远是0b00。
// 因此,在out_of_line_ness == 0b10,就被用于标记out-of-line动态数组的状态。
#define REFERRERS_OUT_OF_LINE 2

struct weak_entry_t {
    /** 指向的对象 */
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            /** 存储weak指针的动态数组的起始地址 */
            weak_referrer_t *referrers;
            /** 标识是否使用了动态数组:占2位 */
            uintptr_t        out_of_line_ness : 2;
            /** 已存的weak指针个数:占62位 */
            uintptr_t        num_refs : PTR_MINUS_2;
            /** 动态数组的总大小 */
            uintptr_t        mask;
            /** hash查找最大偏移量 */
            uintptr_t        max_hash_displacement;
        }; // 五个成员,共32字节
        struct {
            // 结构体中自带的可以存储4个weak指针的静态数组
            // 注:out_of_line_ness域使用了索引为1的位置的最低2位
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        }; // 32字节
    };
    
    /** 判定是否使用了动态数组存储weak指针 */
    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键值对实例(以前没有) */
    weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        : referent(newReferent)
    {
        // 使用静态数组存储weak指针
        inline_referrers[0] = newReferrer;
        // 将数组的其他位置数据置为nil
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }
};

weak_entry_t结构体固定为40字节大小。
其中,为了快速访问weak指针,weak_entry_t中直接设置了一个包含4个元素的静态数组。当指向对象的weak指针超过上限时,就使用动态数组进行存储(将静态数组中的weak指针copy到动态数组中,之后再存入新的weak指针)。
weak_entry_t提供给外部用于识别自身正在使用哪种存储数组的函数。

有了以上这些知识,我们就可以解释SideTable中的weak表是如何注册weak对象并如何清除weak对象的过程了。

4. 在weak引用表中注册weak对象

/** 
 * 如果不存在,注册一个新的(对象,弱指针)键值对(entry对象)。
 * 
 * @param weak_table 全局弱引用表
 * @param referent 弱指针要指向的对象
 * @param referrer 弱指针的地址
 */
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;
    // weak指针
    objc_object **referrer = (objc_object **)referrer_id;

    // 原对象为nil,或者原对象是taggedPointer,直接返回原对象
    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // ensure that the referenced object is viable
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {
        // 若原始对象的类,没有实现内存管理的相关方法
        
        // 直接以SideTable的释放状态作为标识
        deallocating = referent->rootIsDeallocating();
    }
    else {
        // 若原始对象的类,实现了内存管理的相关方法

        
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            // 若allowsWeakReference的实现为消息转发函数,则证明调研对象不支持weak,直接返回nil
            return nil;
        }
        
        // 若allowsWeakReference的实现正常,执行此IMP,返回值取反作为释放的标识(即:自定义为不支持weak,则认为正在释放)
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
    }

    if (deallocating) {
        // 根据释放标识,进行处理
        if (crashIfDeallocating) {
            // crash
            _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 {
            // 返回nil
            return nil;
        }
    }

    // now remember it and where it is being stored
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // 在weak表中,使用原始对象获取生成入口信息,若存在(证明原始对象子表中已经存储了其他weak指针了)
        
        // 将weak指针插入到子表中
        append_referrer(entry, referrer);
    } 
    else {
        // 没有entry对象
        
        // 使用原始对象创建新的entry对象(绑定好key和value)
        weak_entry_t new_entry(referent, referrer);
        
        // 检查是否需要扩充weak表(扩充后,原始数据均插入了新weak表中)
        weak_grow_maybe(weak_table);
        
        // 将新的入口信息插入到weak表中
        weak_entry_insert(weak_table, &new_entry);
    }


    // 这里不要修改referrer的指向(也就是weak指针的指向不能变)

    // 返回原对象
    return referent_id;
}

其中,通过原始对象,在weak表中查找对应entry的过程如下:

/** 
 * 根据给定的对象,返回弱引用表中对应的entry。
 * 如果没有对应的entry,返回NULL。
 * 执行了一次循环查找。
 *
 * @param weak_table 
 * @param referent The object. Must not be nil.
 * 
 * @return The table of weak referrers to this object. 
 */
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    assert(referent);

    // 从表中取出weak_entry_t指针(weak数据表的起始地址)
    weak_entry_t *weak_entries = weak_table->weak_entries;

    if (!weak_entries) return nil;

    // 根据对象地址的hash与weak表的mask与运算得到遍历的起始index
    size_t begin = hash_pointer(referent) & weak_table->mask;
    size_t index = begin;
    size_t hash_displacement = 0;
    
    // 依次查看weak_entry_t对象中的referent是否为指向的对象
    while (weak_table->weak_entries[index].referent != referent) {
        // 不是,索引后移
        index = (index+1) & weak_table->mask;
        
        // 索引又变为begin了,直接crash(hash冲突且没有空余位置了)
        if (index == begin) bad_weak_table(weak_table->weak_entries);
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            // 超出最大偏移范围,返回
            return nil;
        }
    }
    
    // 此时,通过index取到weak_entry_t对象,返回地址
    return &weak_table->weak_entries[index];
}

在查找遍历索引begin时,系统使用的方式是通过对象的hash值和weak_table的mask值进行按位与运算
其中mask是num_entries-1,即如果此弱引用表的weak_entries个数是4,mask即为3,也就是0b11。
对象地址hash之后,通过mask进行
按位与
运算得到的,就只有mask值对应的范围,即0b000b11,故index取值范围是03。系统巧妙地将对象地址转化为索引,在数组中查找对应位置的数据。
后面使用while循环的原因是,由于hash值可能会重复,得到的index位置可能已经存在其他数据,故对index进行偏移处理,待新位置的元素符合要求,再进行操作。其中hash_displacement就是标记偏移量的。如果超过最大偏移量max_hash_displacement,则数组中没有符合要求的位置索引。

向已经存在的weak_entry_t中插入新的weak指针,操作如下:

static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
    if (! entry->out_of_line()) {
        // 若使用的内部数组存储weak指针,则直接遍历,将空的赋值为weak指针地址
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            if (entry->inline_referrers[i] == nil) {
                entry->inline_referrers[i] = new_referrer;
                return;
            }
        }

        // 内部数组已满,此时需要使用动态数组存储(先初始化4个地址空间)
        weak_referrer_t *new_referrers = (weak_referrer_t *)
            calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
        // 将内部数组的四个weak指针copy到动态数组中
        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;
        entry->max_hash_displacement = 0;
    }


    // 本来就在使用动态数组存储的情况
    assert(entry->out_of_line());

    // 占用空间超过 3/4,扩容并插入新weak指针
    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指针
    weak_referrer_t &ref = entry->referrers[index];
    ref = new_referrer;
    // 计数+1
    entry->num_refs++;
}

而对于weak_table_t弱引用表中,没有引用对象指向的weak_entry_t对象时,直接创建新entry,将weak指针写入后,直接将此entry加入到弱引用表中。此过程与查询entry的过程非常相似:

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++;

    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}

5. 在weak弱引用表中移除weak对象

相对的,当指向的对象将要释放时,运行时系统会将注册到weak表中的原始对象相关的所有weak指针信息移除。也就实现了weak自动置为nil的功能。

void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    // 指向的对象
    objc_object *referent = (objc_object *)referent_id;
    // weak指针的地址
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;

    // 指向对象不存在,直接返回(用指向对象的地址作为key进行保存)
    if (!referent) return;

    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        // 使用指向的对象在weak表中查找,获取entry,如果存在
        
        // 在entry中移除weak指针的地址存储
        remove_referrer(entry, referrer);
        
        // 查看入口信息子表中是否还有其他weak指针存储
        bool empty = true;
        if (entry->out_of_line()  &&  entry->num_refs != 0) {
            // 使用了动态数组存储weak指针,且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指针地址存储了
            
            // 在weak表中移除entry信息
            weak_entry_remove(weak_table, entry);
        }
    }

    // 这里不要清除weak指针的地址,objc_storeWeak函数不允许他人更改
}

整体过程与注册weak指针的流程非常相似。查询到weak_entry_t对象后,移除内部的weak指针,最后清除此entry对象即可。

移除entry中指定的weak指针的过程如下(与插入weak指针非常相似):

static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
    if (! entry->out_of_line()) {
        // 没有使用外部动态数组,即weak直接存储在内部静态数组中
        for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            // 查找到weak指针所在的对象
            if (entry->inline_referrers[i] == old_referrer) {
                // 置为nil,完成清除(weak指针自动置nil的功能)
                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;
    }

    // weak指针存储超过了4个,使用了外部的动态数组
    
    // 得到遍历的起始索引
    size_t begin = w_hash_pointer(old_referrer) & (entry->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    // 在动态数组中依次查看,直到找到weak指针存储的位置
    while (entry->referrers[index] != old_referrer) {
        index = (index+1) & entry->mask;
        if (index == begin) bad_weak_table(entry); // 出错crash
        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;
        }
    }
    
    // 此时index所在位置即为weak指针存储的位置,置为nil清除
    entry->referrers[index] = nil;
    // 将索引个数-1
    entry->num_refs--;
}

在weak_table_t中移除weak_entry_t对象就比较简单了:

static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
    // 使用了动态数组存储weak指针,则直接从起始地址进行释放即完成清除(动态数组通过calloc申请的连续地址空间)
    if (entry->out_of_line()) free(entry->referrers);
    
    // 将entry的整个存储空间清零
    bzero(entry, sizeof(*entry));

    // 整体weak表的num_entries计数 - 1
    weak_table->num_entries--;

    // 检查缩小weak表的容量
    weak_compact_maybe(weak_table);
}

这里,weak_compact_maybe函数会检查weak表中entry的存储占用率,根据实际情况释放相应空间。

不仅如此,weak_grow_maybe函数也如此,会根据占用率动态扩大存储空间。其内部实现都是通过weak_resize函数实现的。

#define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)

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_size * sizeof(weak_entry_t) 个字节(new_size * 40)
    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;
    weak_table->num_entries = 0;  // restored by weak_entry_insert below
    
    // 将原始数据copy到新weak表中
    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表中
                weak_entry_insert(weak_table, entry);
            }
        }
        free(old_entries);
    }
}

这里,我们就知道了,weak_table_t中的weak_entries成员实际上是weak_entry_t组成的数组(calloc创建的连续内存空间)。

6. 总结

至此,我们基本了解了创建和释放一个weak弱指针对象所需要做的主要工作。也明白了weak指针在原对象释放后自动置为nil的实现方式。

此外,我们还了解了:

  1. 苹果对于将hash值转化为数组索引index的转换方式及冲突后的索引偏移的操作。
  2. 对于weak_entry_t数据结构中,存储weak指针的两种方式(快速量少用静态数组,量大使用动态数组的可伸缩方式)的分段式存储思想。

7. 参考资料:

相关文章

  • 弱指针weak的实现学习

    1. weak对象的实现函数 首先,看个例子(取自《Objective-C高级编程 iOS与OS X多线程和内存管...

  • weak

    weak的底层实现weak 弱引用的实现方式

  • @property关键字

    1.weak: weak,弱指针,不会让引用计数器+1,如果指向对象被销毁,指针会自动置nil weak原理: r...

  • weak原理

    weak原理 弱引用指针添加到弱引用表。 NSObject.mm 弱引用的指针存储到弱引用表 通过哈希运算找到弱引...

  • iOS中的weak指针

    ObjC runtime是如何实现weak指针的 用strong指针创建weak指针,系统会调用objc_init...

  • __weak和__unsafe_unretained

    __weak和__unsafe_unretained __weak修饰的弱引用,如果指向的对象销毁,那么指针会立马...

  • 属性关键字

    (weak,assign,Strong,copy) weak: 弱指针, 在对象一创建后就会被释放, 继续引用的时...

  • 内存管理面试题

    1.weak实现原理当一个对象被weak指针指向时,这个weak指针会以对象为key存储到一个weak指针数组里面...

  • Object -C weak 和assgin的区别

    首先ARC才有weak assgin是mrc的东西weak,__weak修饰 弱指针,不会让引用计数器+1,如果指...

  • weak指针工作原理

    面试题 weak指针的工作原理?对象没有强引用时,如何实现将指向当前对象的弱指针置为nil。答:当变量被修饰为 w...

网友评论

      本文标题:弱指针weak的实现学习

      本文链接:https://www.haomeiwen.com/subject/kzdmjctx.html