weak实现原理和销毁原理

weak内部实现比较复杂,首先它是一张散列表,散列表中又包含了自旋锁、引用计数、弱引用表.

我们先来看一下,下面的代码

NSObject __weak *test1 = [[NSObject alloc] init];
NSLog(@"test1: %@", test1);
NSObject *temp = [[NSObject alloc] init];
NSObject __weak *test2 = temp;
NSLog(@"test2: %@", test2);
打印:
test1: (null)
test2: <NSObject: 0x6000017204f0>

我们思考一下,为什直接给weak赋值会立马释放,而当用一个临时变量来赋值就数组和链表的区别数组去重方法取到值了呢? 简单来说是因为weak本身没有强引用,如果直接赋值那么值会立马释放掉,而用临时变量赋值,因为临时变量持有了值,所以在当前作用域中,值没有被释放。

那么weak的赋值是如何实现的呢?

我们在赋值处,打一个断点,开启Debug的汇指针c语言编模式

weak实现原理和销毁原理
数组词以看到赋值那里应该是执行的objc_initWeak指针c语言方法
weak实现原理和销毁原理
下一个符号断点,我们可以找到该方法所属的库
weak实现原理和销毁原理
发现这个也在指针数组obj指针万用表怎么读数c的源码中,关于源码获取请看Runtime底层探究部分,这里我们直接进入源码查看

objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }
    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

很显然,我们要到storeWeak中去查看

storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    if (!haveNew) assert(newObj == nil);
    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;

在这里我们看到了有2个SideTable和一个id类型,这个SidTable是什么呢?

struct SideTable {
    spinlock_t slock;//自旋锁
    RefcountMap refcnts;//引用计数Map
    weak_table_t weak_table;//weak表,这个后面用到了再看
    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }
    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

继续回去数组初始化看我们的源码

// 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
    // weak_register_no_lock方法,如果拒绝如果应拒绝weak存储,则返回nil
    // Set is-weakly-referenced bit in refcount table.
    // 设置弱引用位(is-weakly-referenced bit)到引用计数表
    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.
    // 没有新值。存储空间不变。
}

通过这部分源码注释,我们可以大胆的猜想一下。新值是否就是我们要付给weak的值,那么旧值是否就是weak原来的值。我觉得这个可能性还是挺大的。那里我们进去看一下,先看新值的存储吧。

新值存储

进入weak_regappearanceister_no_lockappstore

/**
 * Registers a new (object, weak pointer) pair. Creates a new weak
 * object entry if it does not exist.
 * 登记一个新的键值对(key:object对象,value:weak指针)。如果原来不存在,那么创建一个新的
 * @param weak_table The global weak table.全局的weak表,这里的weak_table对应我们上面的newTable里的weak_table
 * @param referent The object pointed to by the weak reference. weak指针指向的对象
 * @param referrer The weak pointer address.weak指针的地址
 */
id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{

看到方法注释,这证明我们的猜想基本上是正确的了。好了,进去看一实现

// 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));
        // 无法形成弱引用对,这个对象可能已经释放或正在deallocation
    } else {
        return nil;
    }
}

结合注释和错误提示我们可以知道,存储的时候,会先判断对象本身是否源码交易平台可以,已经释放或正在释放中的都不会进行存储了。继变量之间的关系续看,这里就是存储weak与对象关系的地方指针万用表读数图解了,基于方法名和注释,大致猜测了一下流程,

// now remember it and where it is being stored 记录它和它的存储位置
    weak_entry_t *entry;
    // weak_table是弱引用表,referent是weak要引用的对象,这里应该是获取weak_entry_t,如果存在那么添加记录
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        append_referrer(entry, referrer);
    } 
    else {
        // 如果不存在,new一个weak_entry_t
        weak_entry_t new_entry(referent, referrer);
        // 处理weak_table是否需要扩容
        weak_grow_maybe(weak_table);
        // 插入weak_entry_t到weak_table
        weak_entry_insert(weak_table, &new_entry);
    }
    // Do not set *referrer. objc_storeWeak() requires that the 
    // value not change.
    // 如果value没有改变,不需要更新存储
    return referent_id;

到这里我们就需要,看一下weak_table源码编辑器的结构了,通过注释可以知道,有全局源码编程器的弱引用表,其中以对象的地址为key,以数组变量值为value,存储着该对象的所有弱引用weak_ent数组排序ry_t

/**
 * The global weak references table. Stores object ids as keys,
 * and weak_entry_t structs as their values.
 */
struct weak_table_t {
    weak_entry_t *weak_entries;
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

weak_table结构清楚了,我们看一下怎样从weak_table源码中的图片获取weak_entry_t

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 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;
        }
    }
    return &weak_table->weak_entries[index];
}

从begin和index计算和了可以知道,这是在进行哈希计算,所以可以证明weak_table是一张哈希源码1688表。这个获取方法就是,根据对象和哈希函数计算出对应的inde指针x,然后从表中取出对应的weak_entry_t,找不到返回nil。 再看append_refe变量的定义rrer(entry, 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_ness = REFERRERS_OUT_OF_LINE;
        entry->mask = WEAK_INLINE_COUNT-1;
        entry->max_hash_displacement = 0;
    }
    assert(entry->out_of_line());
    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++;
}

这块简单数组和链表的区别来说就是把新的新弱引用指针,添加到对象对应的weak_entry_t里的一个记录数组中。

旧值释放

现在我们来看一下旧值释放部分

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.
}
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 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;
        }
    }
    entry->referrers[index] = nil;
    entry->num_refs--;
}

结合我们上面新值添加分析,出来的结果,这里的操作很容易理解的,先根据旧值,从weak_table中找到旧值对应的wappstoreeak_entry_t,然后从weak_entry_t把弱引用指针从数组中移除就好了。

dealloc

我们知道当弱引用对象释放后,所有的弱引用都会变成nil,这个是如何实现的呢?我们变量值先查找源码的dealloc,然后依次深入- (void)dealloc->void _objc_rootDeall指针oc(id obj)->inline vapproachoid objc_object::rootDealloc()->id object_dispose(id obj)

- (void)dealloc {
    _objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
    assert(obj);
    obj->rootDealloc();
}
inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?
    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}
id 
object_dispose(id obj)
{
    if (!obj) return nil;
    // 销毁实例而不释放内存
    objc_destructInstance(obj);    
    // 释放内存
    free(obj);
    return nil;
}

所以weak的释放,可能在objc_destructInstance(obj);

void *objc_destructInstance(id obj)
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();
        // This order is important.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }
    return obj;
}
inline void 
objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}
void 
objc_object::sidetable_clearDeallocating()
{
    SideTable& table = SideTables()[this];
    // clear any weak table items
    // clear extra retain count and deallocating bit
    // (fixme warn or abort if extra retain count == 0 ?)
    table.lock();
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it != table.refcnts.end()) {
        if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        table.refcnts.erase(it);
    }
    table.unlock();
}

这里我们一步步找到了weak_clear_no_lock(&table.we数组和链表的区别ak_table, (id)this);

void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;
    // 找到weak_entry_t
    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 %pn", 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;
    }
    // 通过循环,把所以指向该对象的weak指针置为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_entry_remove(weak_table, entry);
}

看到了吧,从weak_table中变量类型有哪些找到当前对象对应的weak_entry_t,然后for循环遍历weak_entry_t中存储弱引用指针的数组referrers,把里面全部的weak指针指向置为nil,最后从weak_table中删除相应weak_entry_t

w源码交易平台eak相关的数据关系图数组

weak实现原理和销毁原理

wea变量英语k负责过程图

weak实现原理和销毁原理

dealloc中指针式万用表weak的处理流程图

weak实现原理和销毁原理

发表评论

提供最优质的资源集合

立即查看 了解详情