美文网首页iOS 相关iOSiOS面试问题集
iOS-底层原理26:weak原理

iOS-底层原理26:weak原理

作者: AcmenL | 来源:发表于2021-03-15 22:35 被阅读0次

    1、weak原理概括

    weak是弱引用,用weak描述修饰或者所引用对象的计数器不会加一,并且会在引用的对象被释放的时候自动被设置为nil,大大避免了野指针访问坏内存引起的崩溃情况。

    weak表明该属性定义了一种“非拥有关系”,为这种属性设置新值时,设置方法既不保留新值,也不释放旧值,此特质同assign类似。

    问题:runtime如何实现weak变量的自动置nil?

    解答
    runtimeweak对象放入一个hash表中,key是所指对象的指针,valueweak指针的地址数组(可能存在多个对象引用);
    当对象的引用计数为0时会dealloc,假如weak指向的对象内存地址为a,那么就会以a为键在这个weak表中搜索,找到所有以a为键的weak对象,从而设置为nil。

    2、weak原理实现步骤

    weak的实现原理可概括为三步:

    1、 初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

    2、 添加引用时:objc_initWeak函数会调用objc_storeWeak()函数, objc_storeWeak()的作用是更新指针指向,创建对应的弱引用表。

    3、 释放时:调用clearDeallocating函数,clearDeallocating函数首先会根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个 entryweak 表中删除,最后清理对象的记录。

    3、源码分析

    step1: 可以通过打断点查看汇编的方式查看weak源码

    NSObject *obj = [[NSObject alloc] init];
    id __weak obj1 = obj;
    

    step2: 可以在objc源码中找到objc_initWeak函数

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

    这里先判断对象是否有效,无效直接释放返回;否则通过storeWeak函数注册一个指向为value__weak对象

    step3: storeWeak函数

    static id 
    storeWeak(id *location, objc_object *newObj)
    {
        ASSERT(haveOld  ||  haveNew);
        if (!haveNew) ASSERT(newObj == nil);
    
        Class previouslyInitializedClass = nil;
        id oldObj;
      //声明新旧两个SideTable
        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.
    
    //根据新值和旧值分别获取全局的SideTables表,分别赋值给oldTable,newTable
     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_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;
    }
    
    

    * 旧对象解除注册操作 weak_unregister_no_lock

    该方法主要作用是将旧对象在 weak_table 中解除 weak 指针的对应绑定。根据函数名,称之为解除注册操作。从源码中,可以知道其功能就是从 weak_table 中接触 weak 指针的绑定。而其中的遍历查询,就是针对于 weak_entry 中的多张弱引用散列表。

    * 新对象添加注册操作 weak_register_no_lock

    这一步与上一步相反,通过 weak_register_no_lock 函数把新的对象进行注册操作,完成与对应的弱引用表进行绑定操作

    step4: weak释放为nil过程

    weak被释放为nil,需要对对象整个释放过程了解,如下是对象释放的整体流程:
    1、调用objc_release
    2、因为对象的引用计数为0,所以执行dealloc
    3、在dealloc中,调用了_objc_rootDealloc函数
    4、在_objc_rootDealloc中,调用了object_dispose函数
    5、调用objc_destructInstance
    6、最后调用objc_clear_deallocating。

    对象准备释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址(key)获取所有weak指针地址的数组(value),然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    相关文章

      网友评论

        本文标题:iOS-底层原理26:weak原理

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