其他分享
首页 > 其他分享> > iOS-散列表

iOS-散列表

作者:互联网

一、散列表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(引用计数表)

typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
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(添加引用的时候调用)

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

三、总结

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

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