ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

iOS-散列表

2022-06-30 14:32:23  阅读:133  来源: 互联网

标签:SideTable iOS weak newObj 引用 lock 列表 id


一、散列表sideTable的数据结构

SideTable主要包含spinlock锁,引用计数表(存放从extra_rc接收的一半引用计数),弱引用表。

truct SideTable {
    spinlock_t slock;
    // 存放从extra_rc接收的那一半引用计数
    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<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

1.1 spinlock_t 加锁

spinlock_t 不是自旋锁,在底层代码查找的过程中,我们可以发现他是一把os_unfair_lock锁,在使用sidetable的时候,频繁的读取需要加锁,一张表无疑影响了效率,因此,我们采用stripedMap来分散压力,且stripedMap的数量是根据系统来确定的(真机模式下sidetable最多为8张)。

// 上面 SideTables 的实现
static StripedMap<SideTable>& SideTables() {
    return SideTablesMap.get();
}

1.2RefcountMap(引用计数表)

  • RefcountMap本身从DenseMap得来
typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
  • 存放从extra_rc接收的那一半引用计数
if (variant == RRVariant::Full) {
    if (slowpath(transcribeToSideTable)) {
        // Copy the other half of the retain counts to the side table.
        // 将引用计数一半存在散列表中的方法
        sidetable_addExtraRC_nolock(RC_HALF);
    }
    if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
} else {
    ASSERT(!transcribeToSideTable);
    ASSERT(!sideTableLocked);
}

接着,来看一下 sidetable_addExtraRC_nolock 方法:

bool 
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
    ASSERT(isa.nonpointer);
    // 获取SideTables,也就是StripeMap
    SideTable& table = SideTables()[this];

    size_t& refcntStorage = table.refcnts[this];
    size_t oldRefcnt = refcntStorage;
    // isa-side bits should not be set here
    ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
    ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);

    if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;

    uintptr_t carry;
    size_t newRefcnt = 
        addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
    if (carry) {
        refcntStorage =
            SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
        return true;
    }
    else {
        refcntStorage = newRefcnt;
        return false;
    }
}

1.3 WeakTable(弱引用表)

弱引用底层调用objc_initWeak:

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

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

1.3.1 storeWeak(添加引用的时候调用)

  • storeWeak一共有五个参数,其中3个参数定义在了template模版,
  • weak_unregister_no_lock:清除原来弱引用表中的数据
  • weak_register_no_lock:将weak的指针地址添加到对象的弱引用表
enum CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};

// HaveOld:weak指针是否指向一个弱引用
// HavNew:weak指针是否需要指向一个新的引用
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;
    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<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(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的指针地址添加到对象的弱引用表
            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()) {
            // 将对象曾经指向过若引用的标识置为true,没有弱引用的释放更快
            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);

    // 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;
}

2.2 weak_entry_t

struct weak_table_t {
    // 弱引用数组
    weak_entry_t *weak_entries;
    // 数组个数
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

weak_entries是一个哈希数组,一个对象可以被多个弱引用指针引用,因此,这里用数组的形式表示一个对象的多个弱引用;数组中存储的内容就是弱引用对象的指针地址。当对象的弱引用个数小于等于4时走静态存储(在weak_entry_t初始化的时候一并分配好),大于4走动态存储。

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    // 联合体,对象的弱引用个数若小于等于4,下边的结构体:静态存储,大于4,上边的结构体:动态存储
    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;
        }
    }
};

三、总结

  • sidetables可以理解为一个全局的hash数组,里面存储了sidetables类型的数据,其中长度为8或者64
  • 一个obj(oc对象)对应了一个sideTable,但是一个SideTable,会对应多个obj,因为sidetabels的数量只有8或者64个,所以有很多obj会共用一个sidetable
  • 在弱引用表中,key是对象的地址,value是weak指针地址的数组(weak_entry_t)
  • weak_unregister_no_lock 和 weak_register_no_lock 中都是对 weak_entry_t 类型的数组进行操作

(图片来自:https://juejin.cn/post/7113939858665504776#heading-2)

标签:SideTable,iOS,weak,newObj,引用,lock,列表,id
来源: https://www.cnblogs.com/SNMX/p/16426748.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有