精华内容
下载资源
问答
  • 2021-09-17 11:33:33

    写在最前。。。

     

    请支持原创~~ 

     

    0. 前言

    所谓智能指针,可以从字面上理解为“智能”的指针。具体来讲,智能指针和普通指针的用法是相似的,不同之处在于,智能指针可以在适当时机自动释放分配的内存。也就是说,使用智能指针可以很好地避免“忘记释放内存而导致内存泄漏”问题出现。由此可见,C++ 也逐渐开始支持垃圾回收机制了,尽管目前支持程度还有限。

    c++11 中发布了shared_ptrunique_ptrweak_ptr 用以资源的管理,都是定义在memory 这个头文件中。

    • std::shared_ptr 允许多个shared_ptr 实例指向同一个对象,通过计数管理;
    • std::unique_ptr 是独占对象;
    • weak_ptr 是辅助类,是一种弱引用,指向shared_ptr 所管理的对象。

    1. 源码分析

    1.1 头文件

    #include <memory>

    1.2 构造

        constexpr weak_ptr() noexcept;
        template<class Y> weak_ptr(shared_ptr<Y> const& r) noexcept;
        weak_ptr(weak_ptr const& r) noexcept;
        template<class Y> weak_ptr(weak_ptr<Y> const& r) noexcept;
        weak_ptr(weak_ptr&& r) noexcept;                      // C++14
        template<class Y> weak_ptr(weak_ptr<Y>&& r) noexcept; // C++14

    构造函数相比shared_ptr 和unique_ptr 少了很多,其中移动构造函数是c++14 发布的。

    另外,weak_ptr 中的对象只能通过shared_ptr 或 另一个weak_ptr 实例获取。

    1.2.1 weak_ptr 的移动构造函数

    template<class _Tp>
    inline
    weak_ptr<_Tp>::weak_ptr(weak_ptr&& __r) _NOEXCEPT
        : __ptr_(__r.__ptr_),
          __cntrl_(__r.__cntrl_)
    {
        __r.__ptr_ = 0;
        __r.__cntrl_ = 0;
    }

    同shared_ptr,weak_ptr 中存放一个对象的指针__ptr_ 和用以计数的__cntrl_。

    注意的是__cntrl_ 虽然是可以从shared_ptr 中获取(构造参数为shared_ptr时),但是weak_ptr 并不会增加对象指针的shared 引用计数,而只是增加weak 计数。

    1.2.2  weak_ptr 的拷贝构造函数

    template<class _Tp>
    inline
    weak_ptr<_Tp>::weak_ptr(weak_ptr const& __r) _NOEXCEPT
        : __ptr_(__r.__ptr_),
          __cntrl_(__r.__cntrl_)
    {
        if (__cntrl_)
            __cntrl_->__add_weak();
    }

    会通过__cntrl_ 调用add_weak,而不是add_shared。

    下面举例shared_ptr 通常的创建方式:

    std::weak_ptr<int> wp1;                  //不传入任何实参
    
    std::shared_ptr<int> sp (new int);
    std::weak_ptr<int> wp3 (sp);             //没有直接传对象指针的构造,只能通过shared_ptr 或者weak_ptr

    1.3 赋值重载

        weak_ptr& operator=(weak_ptr const& r) noexcept;
        template<class Y> weak_ptr& operator=(weak_ptr<Y> const& r) noexcept;
        template<class Y> weak_ptr& operator=(shared_ptr<Y> const& r) noexcept;
        weak_ptr& operator=(weak_ptr&& r) noexcept;                      // C++14
        template<class Y> weak_ptr& operator=(weak_ptr<Y>&& r) noexcept; // C++14

    1.4 修改的接口

        void swap(weak_ptr& r) noexcept;
        void reset() noexcept;
    

    1.5 获取

        long use_count() const noexcept;
        bool expired() const noexcept;
        shared_ptr<T> lock() const noexcept;
        template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
        template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;

    对于weak_ptr 的成员函数总结如下:

    成员方法

    功 能

    operator=()

    重载 = 赋值运算符,是的 weak_ptr 指针可以直接被 weak_ptr 或者 shared_ptr 类型指针赋值。

    swap(x)

    其中 x 表示一个同类型的 weak_ptr 类型指针,该函数可以互换 2 个同类型 weak_ptr 指针的内容。

    reset()

    将当前 weak_ptr 指针置为空指针。

    use_count()

    查看指向和当前 weak_ptr 指针相同的 shared_ptr 指针的数量。

    expired()

    判断当前 weak_ptr 指针为否过期(指针为空,或者指向的堆内存已经被释放)。

    lock()

    如果当前 weak_ptr 已经过期,则该函数会返回一个空的 shared_ptr 指针;反之,该函数返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针。

    注意:

    •  weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源;
    •  weak_ptr用于解决”引用计数”模型循环依赖问题,weak_ptr指向一个对象,并不增减该对象的引用计数器。weak_ptr用于配合shared_ptr使用,并不影响动态对象的生命周期,即其存在与否并不影响对象的引用计数器。weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象。weak_ptr提供了expired()与lock()成员函数,前者用于判断weak_ptr指向的对象是否已被销毁,后者返回其所指对象的shared_ptr智能指针(对象销毁时返回”空”shared_ptr);
    • 智能指针是模板类而不是指针;
    • weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例进而访问原始对象
    •  weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shard_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放;
    •  当创建一个weak_ptr时,要用一个shared_ptr来初始化它。不能使用weak_ptr直接访问对象,而必须调用lock。此函数检查weak_ptr指向的对象是否仍存在。如果存在,lock返回一个指向共享对象的shared_ptr。与任何其它shared_ptr类似,只要此shared_ptr存在,它所指向的底层对象也就会一直存在;

    2. 举例

    #include <iostream>
    #include <memory>
     
    std::weak_ptr<int> gw;
     
    void observe()
    {
        std::cout << "use_count == " << gw.use_count() << ": ";
        if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
    	    std::cout << *spt << "\n";
        }
        else {
            std::cout << "gw is expired\n";
        }
    }
     
    int main()
    {
        {
            auto sp = std::make_shared<int>(42);
    	    gw = sp;
     
    	    observe();
        }
     
        observe();
    }

    运行结果:

    use_count == 1: 42
    use_count == 0: gw is expired

    在区域执行完成后,对象会被释放,虽然gw 还在,但是通过lock 获取的shared_ptr 已经为空

    再来看个例子:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main()
    {
        std::shared_ptr<int> sp1(new int(10));
        std::shared_ptr<int> sp2(sp1);
        std::weak_ptr<int> wp(sp2);
        //输出和 wp 同指向的 shared_ptr 类型指针的数量
        cout << wp.use_count() << endl;
        //释放 sp2
        sp2.reset();
        cout << wp.use_count() << endl;
        //借助 lock() 函数,返回一个和 wp 同指向的 shared_ptr 类型指针,获取其存储的数据
        cout << *(wp.lock()) << endl;
        return 0;
    }

    运行结果:

    2
    1
    10

    更多相关内容
  • 主要介绍了 C++11智能指针之weak_ptr详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 学习理解函数加上weak后的变化
  • xx智能指针:防止用户忘记释放掉指针所指的堆空间而造成内存泄漏 当一个对象应该被释放时,指向它的智能指针可以确保自动地释放它 智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。...
  • weak*-开源

    2021-07-11 02:58:41
    弱*项目旨在提供尽可能直接的接口,以极少的开销帮助游戏编程。 它主要针对 linux,但长期目标是支持其他 POSIX 兼容系统。
  • 博文 ARM 之十一__weak 和 __attribute__((weak)) 关键字的使用 的测试代码,备用!
  • weakmall:Weak商城

    2021-04-09 23:30:17
    weakmall Weak商城
  • 主要介绍了解决Chrome在新版MacOS上报错 NET::ERR_CERT_WEAK_KEY 的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • A Fast Overlapping Community Detection Algorithm Based on Weak Cliques for Large-Scale Networks
  • 主要介绍了C++11新特性之智能指针,包括shared_ptr, unique_ptr和weak_ptr的基本使用,感兴趣的小伙伴们可以参考一下
  • 9、Weak Signal Digital GNSS Tracking Algorithms 10、Fundamentals of Global Positioning System Receivers II 11、Composite GNSS Signal Acquisition over Multiple Code Periods 12、GPS接收机电路设计 13...
  • IOS中(assign,retain,copy,weak,strong)的区别以及nonatomic的含义 我们在声明@property 属性时,总是要在括号中写上assign、retain、copy、weak、strong中的一个,很多时候,我们仅仅只是按照习惯去写经常写...
  • Weak signal which is submerged by strong noise in.engineering is difficult to detect accurately. The bistable stochastic.resonance (BSR) system is proposed in this paper to detect the weak.signal. And...
  • weak实现原理

    2021-12-07 19:48:40
    weak 实现原理 下面的一段代码是我们在开发中常见的weak的使用 - (void)viewDidLoad { [super viewDidLoad]; NSObject *obj = [[NSObject alloc] init]; __weak NSObject *weakp = obj; } 汇编 runtime调用objc...

    前言

    看源码可以让我们更加深入理解weak指针。
    源码地址
    weak源码分析流程图

    weak 实现原理

    下面的一段代码是我们在开发中常见的weak的使用

    - (void)viewDidLoad {
        [super viewDidLoad];
         NSObject *obj = [[NSObject alloc] init];
        __weak NSObject *weakp = obj;
    }
    

    转汇编

    请添加图片描述

    objc_initWeak

    objc_initWeak(&weakp, obj)
    

    源码

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

    分析

    • 当我们初始化一个weak变量时,runtime会调用 NSObject.mm 中的objc_initWeak函数

    • objc_initWeak函数前提条件: newObj必须是一个没有被注册为__weak对象的有效指针。而newObj则可以是null,或者指向一个有效的对象

    • 参数location和newObj
      location :__weak指针自己的地址 也就是 &weakp 后面通过设置*location = nil 完成 弱指针 = nil
      newObj :所引用的对象,即例子中的obj

    内存草图

    请添加图片描述

    storeWeak

    objc_initWeak函数会调用storeWeak函数,该函数主要是更新指针指向,创建对应的弱引用表

    storeWeak(&weakp, obj);
    

    源码

    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;
    
     retry:
        // 如果weak指针变量 之前弱引用过一个对象 讲这个对象对应的SideTable从SideTables中取出来,赋值给oldTable
        if (haveOld) {
            oldObj = *location;
            oldTable = &SideTables()[oldObj];
        } else {
            // 如果weak指针变量 之前没有弱引用过一个obj,则oldTable = nil
            oldTable = nil;
        }
        
        //  如果weak指针变量要weak引用一个新的obj,则将该obj对应的SideTable取出,赋值给newTable
        if (haveNew) {
            newTable = &SideTables()[newObj];
        } else {
            // 如果weak指针变量不需要引用一个新obj,则newTable = nil
            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()) //  如果cls还没有初始化,先初始化,再尝试设置weak
            {
                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; // 重新获取一遍newObj,这时的newObj应该已经初始化过了
            }
        }
        
        
        
        // 如果weak指针变量之前弱引用过别的对象oldObj,则调用weak_unregister_no_lock,在oldObj的weak_entry_t中移除该weak_ptr地址
    
        if (haveOld) {
            weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
        }
        
        // 如果weak指针变量需要弱引用新的对象newObj
        if (haveNew) {
            
            // 1 调用weak_register_no_lock方法,weak_register_no_lock会将weak指针变量的地址 记录到newObj对应的weak_entry_t中
            newObj = (objc_object *)
                weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
                                      crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating);
    
            // 2 更新newObj的isa的weakly_referenced bit标志位
            if (!newObj->isTaggedPointerOrNil()) {
                newObj->setWeaklyReferenced_nolock();
            }
            
            // 3 *location 赋值,也就是将weak指针变量直接指向了newObj   这里并没有将newObj的引用计数+1 , 所以weak引用不会让newObj引用计数+1
            *location = (id)newObj;  // 也就是例子中 将weakp 指向obj
        }
        else {
            // No new value. The storage is not changed.
        }
        
        // 解锁,其他线程可以访问oldTable, newTable了
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    
    .
        // 返回newObj,此时的newObj与刚传入时相比,weakly-referenced bit位置1
        callSetWeaklyReferenced((id)newObj);
    
        return (id)newObj;
    }
    
    

    分析

    • storeWeak方法实际上是接收了5个参数,分别是haveOld、haveNew和crashIfDeallocating ,这三个参数都是以模板的方式传入的,是三个bool类型的参数。 分别表示weak指针之前是否指向了一个弱引用,weak指针是否需要指向一个新的引用,若果被弱引用的对象正在析构,此时再弱引用该对象是否应该crash

    • 该方法维护了oldTable 和newTable分别表示旧的引用弱表和新的弱引用表,它们都是SideTable的hash表。

    • 对两个表进行加锁操作,防止多线程竞争冲突

    • 判断其isa 是否为空,为空则需要进行初始化

    • 如果存在旧值,调用 weak_unregister_no_lock 函数清除旧值

    • 调用 weak_register_no_lock 函数分配新值 也就是将weak指针自己的地址添加到对象弱引用表中。

    • 解锁两个表,其他线程可以访问oldTable, newTable

    • 调用setWeaklyReferenced_nolock 方法修改weak指针变量新引用的对象的bit标志位

    这个方法中重点是weak_unregister_no_lock 和weak_register_no_lock 这两个方法。而这两个方法都是操作 SideTable结构体 ,那么我们需要先来了解下SideTable 。

    数据结构

    SideTables

    Q:SideTables里面的SideTable 和object是不是1对1关系?
    SideTables是一个hash数组,里面存储了SideTable。SideTables的hash键值就是一个对象obj的address。 因此可以说,一个obj,对应了一个SideTable。但是一个SideTable,会对应多个obj。因为SideTable的数量有限,所以会有很多obj共用同一个SideTable

    如图

    请添加图片描述

    Q:如何从sideTables里找到特定的sideTable?
    这就用到了散列函数。runtime是通过下面一个函数来获取到相应的sideTable:
    例如:oldTable = &SideTables()[oldObj];

    table = &SideTables()[obj];
    static StripedMap<SideTable>& SideTables() {
        return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
    }
    

    SideTable

    请添加图片描述

    struct SideTable {
        spinlock_t slock;
        RefcountMap refcnts;
        weak_table_t weak_table;
    }
    

    分析

    • spinlock_t slock : 自旋锁,用于上锁/解锁 SideTable。
    • refcnts 用来存储OC对象的引用计数的hash表(未开启isa优化才会用,现在的isa 已经不是一个单纯的指针了 Tagged Pointer是苹果最新推出的指针优化,ISA其实并不单单是一个指针
      如果该对象不是Tagged Pointer且关闭了Non-pointer,那该对象的引用计数就使用SideTable来存)查看Tagged Pointer 看我这篇文章
    • weak_table_t weak_table : 存储对象弱引用指针的hash表,OC中weak功能实现都在这个数据结构中。

    Q: 为什么不直接用一张SideTable,而是用SideTables去管理多个SideTable?
    SideTable里有一个自旋锁,如果把所有的类都放在同一个SideTable,有任何一个类有改动都会对整个table做操作,并且在操作一个类的同时,操作别的类会被锁住等待,这样会导致操作效率和查询效率都很低。而有多个SideTable的话,操作的都是单个Table,并不会影响其他的table,这就是分离锁。

    weak_table_t

    struct weak_table_t {
    
    // hash数组,用来存储弱引用对象的相关信息weak_entry_t   
    weak_entry_t *weak_entries;
     
    //weak_entries的数目  即weak_table_t这个表被多少oc对象共同用
    size_t num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
    };
    
    

    分析

    • weak_table_t是一个典型的hash结构

    • weak_entries: hash数组,用来存储若干weak_entry_t,每一个弱引用对象的相关信息都被包装成一个weak_entry_t 结构体,数组可动态扩容、缩容

    • num_entries: hash数组中的元素个数

    • mask:hash数组长度-1,会参与hash计算。(注意,这里是hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个

    • max_hash_displacement:可能会发生的hash冲突的最大次数,用于判断是否出现了逻辑错误(hash表中的冲突次数绝不会超过改值)

    weak_entry_t

    #define WEAK_INLINE_COUNT 4
    #define REFERRERS_OUT_OF_LINE 2
    
    struct weak_entry_t {
        DisguisedPtr<objc_object> referent; // 被弱引用的对象
        
        // 引用该对象的对象列表,联合。 引用个数小于4,用inline_referrers数组。 引用个数大于4,用动态数组weak_referrer_t *referrers
        union {
            struct {
                weak_referrer_t *referrers;                      // 弱引用该对象的对象指针地址的hash数组
                uintptr_t        out_of_line_ness : 2;           // 是否使用动态hash数组标记位
                uintptr_t        num_refs : PTR_MINUS_2;         // hash数组中的元素个数
                uintptr_t        mask;                           // hash数组长度-1,会参与hash计算。(注意,这里是hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个)素个数)。
                uintptr_t        max_hash_displacement;          // 可能会发生的hash冲突的最大次数,用于判断是否出现了逻辑错误(hash表中的冲突次数绝不会超过改值)
            };
            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;
            }
        }
    };
    

    分析

    • weak_entry_t的结构也是⼀个hash结构 对象的地址的 hash 化后的数值作为 key,⽤ weak_entry_t 类型的结构体对象作为 value

    • weak_entry_t的结构和weak_table_t很像,同样也是一个hash表,其存储的元素是weak_referrer_t,实质上是弱引用该对象的指针的指针,即 objc_object **new_referrer , 通过操作指针的指针,就可以使得weak 引用的指针在对象析构后,指向nil。

    • 可以看到在weak_entry_t 的结构定义中有联合体,在联合体的内部有定长数组inline_referrers[4]和动态数组weak_referrer_t *referrers两种方式来存储弱引用对象的指针地址。

    • 通过out_of_line()这样一个函数方法来判断采用哪种存储方式。当弱引用该对象的指针数目小于等于4时,使用定长数组。当超过4时,会将定长数组中的元素转移到动态数组中,并之后都是用动态数组存储。

    到这里我们已经清楚了弱引用表的结构是一个hash结构的表,Key是所指对象的地址,Value是weak_table_t类型的结构体 这个结构体内部也是一个hash结构的表,key是对象地址 Value是一个存放 weak指针自己地址的数组 。那么接下来看看这个弱引用表是怎么维护这些数据的。

    weak_register_no_lock 添加弱引用

    源码

    id weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                          id *referrer_id, bool crashIfDeallocating)
    {
        // referent指向是对象地址  referrer指向weak指针自己的地址
        objc_object *referent = (objc_object *)referent_id;
        objc_object **referrer = (objc_object **)referrer_id;
    
        // 如果referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回,不做任何操作
        if (!referent  ||  referent->isTaggedPointer()) return referent_id;
    
        // 确保被引用的对象可用(没有在析构,同时应该支持weak引用)
        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));
            } else {
                return nil;
            }
        }
    
        // now remember it and where it is being stored
        // 在 weak_table中找到referent对应的weak_entry,并将referrer加入到weak_entry中
        weak_entry_t *entry;
        if ((entry = weak_entry_for_referent(weak_table, referent))) { // 如果能找到weak_entry,则讲referrer插入到weak_entry中
            append_referrer(entry, referrer); 	// 将referrer插入到weak_entry_t的引用数组中
        } 
        else { // 如果找不到,就新建一个
            weak_entry_t new_entry(referent, referrer);  
            weak_grow_maybe(weak_table);
            weak_entry_insert(weak_table, &new_entry);
        }
    
        // Do not set *referrer. objc_storeWeak() requires that the 
        // value not change.
    
        return referent_id;
    }
    
    

    这个方法需要传入四个参数,它们代表的意义如下:

    • weak_table:weak_table_t 结构类型的弱引用表。
    • referent_id:weak指针。
    • *referrer_id:weak指针地址。
    • crashIfDeallocating :若果被弱引用的对象正在析构,此时再弱引用该对象是否应该crash。

    分析

    • 如果referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回,不做任何操作。
    • 如果对象正在析构,则抛出异常。
    • 如果对象不能被weak引用,直接返回nil。
    • 如果对象没有在析构且可以被weak引用,则调用weak_entry_for_referent 方法根据弱引用对象的地址从弱引用表中找到对应的weak_entry,如果能够找到则调用append_referrer 方法向其中插入weak指针自己的地址。否则新建一个weak_entry。

    流程图
    请添加图片描述

    weak_entry_for_referent

    在weak_table中找出referent对应的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;  // 这里通过 & weak_table->mask的位操作,来确保index不会越界
        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); // 触发bad weak table crash
            hash_displacement++;
            if (hash_displacement > weak_table->max_hash_displacement) { // 当hash冲突超过了可能的max hash 冲突时,说明元素没有在hash表中,返回nil 
                return nil;
            }
        }
        
        return &weak_table->weak_entries[index];
    }
    
    

    分析

    • size_t begin = hash_pointer(referent) & weak_table->mask;
      来尝试确定hash的初始位置。注意,这里做了& weak_table->mask 位操作来确保index不会越界,这同我们平时用到的取余%操作是一样的功能。只不过这里改用了位操作,提升了效率。

    • 然后,就开始对比hash表中的数据是否与目标数据相等
      while (weak_table->weak_entries[index].referent != referent),如果不相等,则index +1, 直到index == begin(绕了一圈)或超过了可能的hash冲突最大值

    这是weak_table_t如何进行hash定位的相关操作。

    append_referrer

    讲referrer添加到weak_entry_t

    static void append_referrer(weak_entry_t *entry, objc_object **new_referrer)
    {
        if (! entry->out_of_line()) { // 如果weak_entry 尚未使用动态数组,走这里
            // 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;
                }
            }
            
            // 如果inline_referrers的位置已经存满了,则要转型为referrers,做动态数组。
            // 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) { // 如果动态数组中元素个数大于或等于数组位置总空间的3/4,则扩展数组空间为当前长度的一倍
            return grow_refs_and_insert(entry, new_referrer); // 扩容,并插入
        }
        
        // 如果不需要扩容,直接插入到weak_entry中
        // 注意,weak_entry是一个哈希表,key:w_hash_pointer(new_referrer) value: new_referrer
        
        // 细心的人可能注意到了,这里weak_entry_t 的hash算法和 weak_table_t的hash算法是一样的,同时扩容/减容的算法也是一样的
        size_t begin = w_hash_pointer(new_referrer) & (entry->mask); // '& (entry->mask)' 确保了 begin的位置只能大于或等于 数组的长度
        size_t index = begin;  // 初始的hash index
        size_t hash_displacement = 0;  // 用于记录hash冲突的次数,也就是hash再位移的次数
        while (entry->referrers[index] != nil) {
            hash_displacement++;
            index = (index+1) & entry->mask;  // index + 1, 移到下一个位置,再试一次能否插入。(这里要考虑到entry->mask取值,一定是:0x111, 0x1111, 0x11111, ... ,因为数组每次都是*2增长,即8, 16, 32,对应动态数组空间长度-1的mask,也就是前面的取值。)
            if (index == begin) bad_weak_table(entry); // index == begin 意味着数组绕了一圈都没有找到合适位置,这时候一定是出了什么问题。
        }
        if (hash_displacement > entry->max_hash_displacement) { // 记录最大的hash冲突次数, max_hash_displacement意味着: 我们尝试至多max_hash_displacement次,肯定能够找到object对应的hash位置
            entry->max_hash_displacement = hash_displacement;
        }
        // 将ref存入hash数组,同时,更新元素个数num_refs
        weak_referrer_t &ref = entry->referrers[index];
        ref = new_referrer;
        entry->num_refs++;
    }
    
    

    分析

    首先确定是使用定长数组还是动态数组,如果是使用定长数组,则直接将weak指针地址添加到数组即可,如果定长数组已经用尽,则需要将定长数组中的元素转存到动态数组中。

    grow_refs_and_insert

    扩容动态数组添加referrer

    __attribute__((noinline, used))
    static void grow_refs_and_insert(weak_entry_t *entry, 
                                     objc_object **new_referrer)
    {
        assert(entry->out_of_line());
    
        size_t old_size = TABLE_SIZE(entry);
        size_t new_size = old_size ? old_size * 2 : 8;
    
        size_t num_refs = entry->num_refs;
        weak_referrer_t *old_refs = entry->referrers;
        entry->mask = new_size - 1;
        
        entry->referrers = (weak_referrer_t *)
            calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));
        entry->num_refs = 0;
        entry->max_hash_displacement = 0;
        
        这里可以看到,旧的数据需要依次转移到新的内存中
        for (size_t i = 0; i < old_size && num_refs > 0; i++) {
            if (old_refs[i] != nil) {
                append_referrer(entry, old_refs[i]); // // 将旧的数据转移到新的动态数组中
                num_refs--;
            }
        }
        // Insert
        append_referrer(entry, new_referrer);
        if (old_refs) free(old_refs);  // // 释放旧的内存
    }
    

    分析

    • 每次扩容是之前的2倍
    • 每一次动态数组的扩容,都需要将旧的数据重新插入到新的数组中。

    weak_unregister_no_lock移除引用

    如果weak指针之前指向了一个弱引用,则会调用weak_unregister_no_lock方法将旧的weak指针地址移除。

    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))) { // 查找到referent所对应的weak_entry_t
            remove_referrer(entry, referrer);  // 在referent所对应的weak_entry_t的hash数组中,移除referrer
           
            // 移除元素之后, 要检查一下weak_entry_t的hash数组是否已经空了
            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_t的hash数组已经空了,则需要将weak_entry_t从weak_table中移除
                weak_entry_remove(weak_table, entry);
            }
        }
    
    

    分析

    • 在weak_table中找出referent对应的weak_entry_t
      在weak_entry_t中移除referrer
    • 移除元素后,判断此时weak_entry_t中是否还有元素 (empty==true?)
      如果此时weak_entry_t已经没有元素了,将weak_entry_t从weak_table中移除

    remove_referrer

    在weak_entry_t中移除referrer

    weak_entry_remove

    将当前weak_entry_t 从weak_table中移除

    static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
    {
        // remove entry
        if (entry->out_of_line()) free(entry->referrers);
        bzero(entry, sizeof(*entry));
    
        weak_table->num_entries--;
    
        weak_compact_maybe(weak_table);
    }
    

    weak_compact_maybe

    将weak_table减容

    static void weak_compact_maybe(weak_table_t *weak_table)
    {
        size_t old_size = TABLE_SIZE(weak_table);
    
        // Shrink if larger than 1024 buckets and at most 1/16 full.
        if (old_size >= 1024  && old_size / 16 >= weak_table->num_entries) {
            weak_resize(weak_table, old_size / 8);
            // leaves new table no more than 1/2 full
        }
    }
    

    weak_resize

    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;
        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
        
        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_entry_insert(weak_table, entry);
                }
            }
            free(old_entries);
        }
    }
    

    weak_entry_insert

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

    流程图:
    请添加图片描述

    weak_entry_remove 到 weak_resize 的流程图
    请添加图片描述

    callSetWeaklyReferenced

    static void callSetWeaklyReferenced(id obj) {
        if (!obj)
            return;
    
        Class cls = obj->getIsa();
    
        if (slowpath(cls->hasCustomRR() && !object_isClass(obj))) {
            ASSERT(((objc_class *)cls)->isInitializing() || ((objc_class *)cls)->isInitialized());
            void (*setWeaklyReferenced)(id, SEL) = (void(*)(id, SEL))
            class_getMethodImplementation(cls, @selector(_setWeaklyReferenced));
            if ((IMP)setWeaklyReferenced != _objc_msgForward) {
              (*setWeaklyReferenced)(obj, @selector(_setWeaklyReferenced));
            }
        }
    }
    

    设置当前对象 weakly_referenced = true

    到这里为止就是对于一个对象做weak引用时底层做的事情,用weak引用对象后引用计数并不会加1,当对象释放时,所有weak引用它的指针又是如何自动设置为nil的呢?

    dealloc

    当对象的引用计数为0时,底层会调用_objc_rootDealloc方法对对象进行释放,而在_objc_rootDealloc方法里面会调用rootDealloc方法。如下是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);
        }
    }
    
    

    分析

    • 首先判断对象是否是Tagged Pointer,如果是则直接返回。
    • 如果对象是采用了优化的isa计数方式,且同时满足对象没有被weak引用!isa.weakly_referenced、没有关联对象!isa.has_assoc 、没有自定义的C++析构方法!isa.has_cxx_dtor、没有用到SideTable来引用计数!isa.has_sidetable_rc则直接快速释放。
    • 如果不能满足2中的条件,则会调用object_dispose 方法。

    object_dispose

    object_dispose 方法很简单,主要是内部调用了objc_destructInstance方法。

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

    分析

    • 如果有自定义的C++析构方法,则调用C++析构函数。
    • 如果有关联对象(比如类别),则移除关联对象并将其自身从Association Manager的map中移除。
    • 调用clearDeallocating 方法清除对象的相关引用。

    clearDeallocating

    源码

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

    分析

    • 判断对象是否采用了优化isa引用计数,如果没有的话则需要清理对象存储在SideTable中的引用计数数据。
    • 如果对象采用了优化isa引用计数,则判断是否有使用SideTable的辅助引用计数(isa.has_sidetable_rc)或者有weak引用(isa.weakly_referenced),符合这两种情况中一种的,调用clearDeallocating_slow 方法。

    clearDeallocating_slow

    NEVER_INLINE void
    objc_object::clearDeallocating_slow()
    {
        assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
    
        SideTable& table = SideTables()[this]; // 在全局的SideTables中,以this指针为key,找到对应的SideTable
        table.lock();
        if (isa.weakly_referenced) { // 如果obj被弱引用
            weak_clear_no_lock(&table.weak_table, (id)this); // 在SideTable的weak_table中对this进行清理工作
        }
        if (isa.has_sidetable_rc) { // 如果采用了SideTable做引用计数
            table.refcnts.erase(this); // 在SideTable的引用计数中移除this
        }
        table.unlock();
    }
    
    

    在这里我们关心的是weak_clear_no_lock 方法。这里调用了weak_clear_no_lock来做weak_table的清理工作。

    weak_clear_no_lock

    源码

    void 
    weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
    {
        objc_object *referent = (objc_object *)referent_id;
        
        // 找到referent在weak_table中对应的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 %p\n", referent);
            return;
        }
    
        // zero out references
        weak_referrer_t *referrers;
        size_t count;
        
        // 找出weak引用referent的weak指针地址数组以及数组长度
        if (entry->out_of_line()) {
            referrers = entry->referrers;
            count = TABLE_SIZE(entry);
        } 
        else {
            referrers = entry->inline_referrers;
            count = WEAK_INLINE_COUNT;
        }
        
        for (size_t i = 0; i < count; ++i) {
            objc_object **referrer = referrers[i]; // 取出每个weak 指针变量的地址
            if (referrer) {
                if (*referrer == referent) { // 如果weak 指针变量确实weak引用了referent,则将weak指针变量设置为nil,这也就是为什么weak 指针会自动设置为nil的原因
                    *referrer = nil;
                }
                else if (*referrer) { // 如果所存储的weak 指针变量没有weak 引用referent,这可能是由于runtime代码的逻辑错误引起的,报错
                    _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); // 由于referent要被释放了,因此referent的weak_entry_t也要移除出weak_table
    }
    
    

    分析

    • 移除关联对象
    • 清理对象存储在SideTable中的引用计数
    • 在全局的SideTables中,以this指针为key,找到对应的SideTable
    • isa.weakly_referenced 如果对象被弱引用,在SideTable的weak_table中对this进行清理工作
    • referent就是 this 就是弱引用对象
    • 找到referent在weak_table中对应的weak_entry_t
    • 在entry中找到referent的weak指针地址数组以及数组长度
    • 取出每个weak指针变量的地址
    • referrer是指针自己的地址 *referrer是对象地址 比较 *referrer ==referent 如果weak指针变量确实weak引用了referent,则将referrer指针变量=nil

    寻址扩容

    源码

        weak_entry_t *entry;
        // 如果能找到weak_entry,则讲referrer插入到weak_entry中
        if ((entry = weak_entry_for_referent(weak_table, referent))) { 
            // 将referrer插入到weak_entry_t的引用数组中
            append_referrer(entry, referrer); 	  } 
        else { // 如果找不到,就新建一个
            weak_entry_t new_entry(referent, referrer);  
            weak_grow_maybe(weak_table);
            weak_entry_insert(weak_table, &new_entry);
        }
    
    

    往weak_table中插入weak_entry的逻辑在weak_entry_insert(…)中:

    index = hash & (length-1)
    在这个方法中,先用对象地址(referent)求hash,再和mask按位与计算的得到起始索引,mask的值为数组长度-1,这样求索引的值必然在数组的范围内。

    比如: 某个对象的地址求得的hash为0x6a9f2be7,当前数组长度为16,mask为15(0b1111),则 0x6a9f2be7 & ob1111 = 0b0111(7),那么begin索引值为7。

    那么,当在初始索引处已经被占用了,也就是发生了hash冲突,则index+1继续往后寻找,直到找到为止,这种处理hash冲突的方法叫做开放寻址法。

    通过对象地址查找weak_entry的时候也是大同小异,具体逻辑在weak_entry_for_referent(…)中,这边不再赘述。

    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) { // 如果能找到weak_entry,则讲referrer插入到weak_entry中
        append_referrer(entry, referrer); 	// 将referrer插入到weak_entry_t的引用数组中
    } 
    

    weak主要查找流程

    寻找 SideTable

    newTable = &SideTables()[newObj];
    

    寻找 weak_table

    &newTable->weak_table
    

    寻找weak_entry_t

    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
    }
    return &weak_table->weak_entries[index];
    

    寻找weak_entry_t下 所有的 weak 指针

    referrer = entry->inline_referrers[i]
    

    概括一下weak引用的存储结构:
    1、SideTable持有的weak_table_t,可以通过对象地址作为key找到, 一个SideTable多个对象用。
    2、weak_table_t内部存放weak_entry_t类型的数组,一个对象对应一个weak_entry_t,一个weak_table_t多个对象用。
    3、weak_entry_t中实际存放着被引用的对象和引用它的所有weak指针的地址。

    weak面试问题

    SideTable怎么从SideTables取出来的?

    // StripedMap is a map of void* -> T, sized appropriately
    StripedMap 是一个以void *为hash key, T为vaule的hash表
    hash定位的算法如下

    static unsigned int indexForPointer(const void *p) {
        uintptr_t addr = reinterpret_cast<uintptr_t>(p);
        return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
    }
    

    把地址指针右移4位异或地址指针右移9位,为什么这么做,也不用关心。我们只要关心重点是最后的值要取余StripeCount,来防止index越界就好。

    SideTable能不能被析构?

    // 构造函数
    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }
    
    //析构函数(看看函数体,苹果设计的SideTable其实不希望被析构,不然会引起fatal 错误)
    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }
    

    通过析构函数可以知道,SideTable不能被析构。

    &SideTables()[oldObj]这是什么?

    实现了一个类 StripedMap 重载了[]操作符

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

    weak_table 是公用的吗?

    weak_table_t weak_table 用来存储OC对象弱引用的相关信息。我们知道,SideTables一共只有64个节点,而在我们的APP中,一般都会不只有64个对象,因此,多个对象一定会重用同一个SideTable节点,也就是说,一个weak_table会存储多个对象的弱引用信息。因此在一个SideTable中,又会通过weak_table作为hash表再次分散存储每一个对象的弱引用信息。

    如图
    请添加图片描述

    weak_table弱引用表谁是key 谁是value 怎么构成哈希表的?

    使用不定类型对象的地址作为key ,用 weak_entry_t 类型结构体对象作为 value

    weak_entry_t 的结构中 weak_referrer_t是什么?

    weak_referrer_t 是二维 objc_object 的别名 是每一个weak指针自己的地址

    NSObject *obj = [[NSObject alloc] init];
    __weak NSObject *weakp = obj;
    

    &weakp 就是一个 weak_referrer_t 对象 ,被装在静态数组或者动态数组中 ,通过 weak_entry_t 可以获取这个数组

    objc_object *referent 和 referrer 都是什么?

    referent 是对象地址
    referrer 是weak指针自己的地址

    我们要结合remove_referrer这个函数来理解

    for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
        if (entry->inline_referrers[i] == old_referrer) {
            entry->inline_referrers[i] = nil;
            return;
        }
    }
    

    如图
    请添加图片描述

    // p对象
    NSObject *p = [[NSObject alloc] init];
    __weak NSObject *p1 = p;
    
    referent_id 就是 p  referrer_id 就是 &p1
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;
    referent 就是 p   *referrer 就是 p1   
    *referrer=nil  也就是 p1=nil
    

    weak_unregister_no_lock中 为什么不把referrer设置为nil?

    这里不会设置 *referrer = nil,因为 objc_storeWeak() 函数会需要该指针

    现在p和p2的弱引用个数分别是几?

    NSObject *p = [[NSObject alloc] init];
    __weak NSObject *weakp = p;
    p对象弱引用个数是1 
    
    NSObject *p2 = [[NSObject alloc] init];
    weakp = p2;
    p对象弱引用个数是0   p2 对象弱引用个数是1
    

    为什么不直接用一张SideTable,而是用SideTables去管理多个SideTable??

    SideTable里有一个自旋锁,如果把所有的类都放在同一个SideTable,有任何一个类有改动都会对整个table做操作,并且在操作一个类的同时,操作别的类会被锁住等待,这样会导致操作效率和查询效率都很低。而有多个SideTable的话,操作的都是单个Table,并不会影响其他的table,这就是分离锁

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

    runtime 对注册的类会进行布局,对于 weak 修饰的对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。

    更细一点的回答:

    1.初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
    2.添加引用时:objc_initWeak函数会调用objc_storeWeak() 函数, objc_storeWeak()的作用是更新指针指向,创建对应的弱引用表。
    3.释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    总结

    • weak的原理在于底层维护了一张weak_table_t结构的hash表,key是对象的地址,value是weak指针的地址数组(其实是一个weak_entry_t结构体,里面有一个数组,数组里面存放的是全是weak指针变量自己的地址)。
    • weak 关键字的作用是弱引用,所引用对象的计数器不会加1,并在引用对象被释放的时候自动被设置为 nil。
    • 对象释放时,调用clearDeallocating函数根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除。
    • ⼀个weak引⽤的处理涉及各种查表、添加与删除操作,还是有⼀定消耗的。所以如果⼤量使⽤__weak变量的话,会对性能造成⼀定的影响
    展开全文
  • We show that weak measurements can be used to measure the tiny signature of topological phase transitions. The signature is an in-plane photonic spin Hall effect, which can be described as a ...
  • 是一本很好的讲授Large Deviation理论的书籍,对于理解、应用Large Deviation理论具有很大的帮助!
  • In this paper, we put forward a weak blind quantum signature scheme based on quantum entanglement swapping of Bell states. Different from the existing quantum signature schemes, our scheme can offer ...
  • 对iOS的weak弱引用的底层方法都加了注释 尤其是objc-weak.mm文件中的方法做了全面的注释 一、DisguisedPtr伪装指针类介绍 主要是用来把对象的指针映射到long类型的数值,来保存对象的指针,至于为什么不直接保存指针,...

    对iOS的weak弱引用的底层方法都加了注释
    尤其是objc-weak.mm文件中的方法做了全面的注释

    一、DisguisedPtr伪装指针类介绍

    主要是用来把对象的指针映射到long类型的数值,来保存对象的指针,至于为什么不直接保存指针,估计是处于安全考虑,
    防止空指针造成的坏的影响;

    地层大量使用了DisguisedPtr,DisguisedPtr也不是很复杂;

    可以看出DisguisedPtr是个模版类,可以看作是iOS中的范型,
    里面定义了一个属性value,用来保存处理后的对象指针;是个unsigned long类型,
    和其他几个函数;

    template <typename T>
    class DisguisedPtr {
    
        uintptr_t value;//用来保存处理后的对象指针;是个unsigned long类型,
    
    
        static uintptr_t disguise(T* ptr) {
        //把对象的指针转成unsigned long类型
            return -(uintptr_t)ptr;
        }
    
        static T* undisguise(uintptr_t val) {
        //还原对象指针
            return (T*)-val;
        }
    
     public:
    
        DisguisedPtr() { }
        //构造函数,通过disguise函数转换之后,把对象指针保存在value中
        DisguisedPtr(T* ptr) 
            : value(disguise(ptr)) { }
        DisguisedPtr(const DisguisedPtr<T>& ptr) 
            : value(ptr.value) { }
    
        DisguisedPtr<T>& operator = (T* rhs) {
            value = disguise(rhs);
            return *this;
        }
        DisguisedPtr<T>& operator = (const DisguisedPtr<T>& rhs) {
            value = rhs.value;
            return *this;
        }
    
    //重载了几个运算符,使用undisguise函数转换后,用来获取保存的对象指针,
        operator T* () const {
            return undisguise(value);
        }
        T* operator -> () const { 
            return undisguise(value);
        }
        T& operator * () const { 
            return *undisguise(value);
        }
        T& operator [] (size_t i) const {
            return undisguise(value)[i];
        }
    
        // pointer arithmetic operators omitted 
        // because we don't currently use them anywhere
    };
    

    二、StripedMap介绍
    StripedMap中默认保存8张表,使用哈希计数对象对应的表

    enum { CacheLineSize = 64 };
    
    // StripedMap<T> is a map of void* -> T, sized appropriately 
    // for cache-friendly lock striping. 
    // For example, this may be used as StripedMap<spinlock_t>
    // or as StripedMap<SomeStruct> where SomeStruct stores a spin lock.
    //保存8张SideTable表,通过哈希对象的指针,确定保存的表的位置,
    template<typename T>
    class StripedMap {
    #if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
        enum { StripeCount = 8 };//苹果手机iOS系统默认8张
    #else
        enum { StripeCount = 64 };
    #endif
    
        //里面又定义一个结构体,value属性就是SideTable,使用64字节对齐
        struct PaddedT {
            T value alignas(CacheLineSize);
        };
    
        //保存8张表的数组
        PaddedT array[StripeCount];
    
        //通过哈希对象的指针计算出array的索引位置
        static unsigned int indexForPointer(const void *p) {
            uintptr_t addr = reinterpret_cast<uintptr_t>(p);
            return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
        }
    
     public:
        //重载运算符,用来获取对应的表
        T& operator[] (const void *p) { 
            return array[indexForPointer(p)].value; 
        }
        const T& operator[] (const void *p) const { 
            return const_cast<StripedMap<T>>(this)[p]; 
        }
    
        // Shortcuts for StripedMaps of locks.
        void lockAll() {
            for (unsigned int i = 0; i < StripeCount; i++) {
                array[i].value.lock();
            }
        }
    
        void unlockAll() {
            for (unsigned int i = 0; i < StripeCount; i++) {
                array[i].value.unlock();
            }
        }
    
        void forceResetAll() {
            for (unsigned int i = 0; i < StripeCount; i++) {
                array[i].value.forceReset();
            }
        }
    
        void defineLockOrder() {
            for (unsigned int i = 1; i < StripeCount; i++) {
                lockdebug_lock_precedes_lock(&array[i-1].value, &array[i].value);
            }
        }
    
        void precedeLock(const void *newlock) {
            // assumes defineLockOrder is also called
            lockdebug_lock_precedes_lock(&array[StripeCount-1].value, newlock);
        }
    
        void succeedLock(const void *oldlock) {
            // assumes defineLockOrder is also called
            lockdebug_lock_precedes_lock(oldlock, &array[0].value);
        }
    
        const void *getLock(int i) {
            if (i < StripeCount) return &array[i].value;
            else return nil;
        }
        
    #if DEBUG
        StripedMap() {
            // Verify alignment expectations.
            uintptr_t base = (uintptr_t)&array[0].value;
            uintptr_t delta = (uintptr_t)&array[1].value - base;
            assert(delta % CacheLineSize == 0);
            assert(base % CacheLineSize == 0);
        }
    #else
        constexpr StripedMap() {}
    #endif
    };
    
    //初始化一个全局对象数组,用来保存引用计数和弱引用计数
    //可以看出这个数组的内存长度等于StripedMap对象所占的内存长度
    //所以这个数据也可以看作是StripedMap对象
    alignas(StripedMap<SideTable>) static uint8_t 
        SideTableBuf[sizeof(StripedMap<SideTable>)];
    //初始化函数,StripedMap保存了8张SideTable表
    static void SideTableInit() {
        new (SideTableBuf) StripedMap<SideTable>();
    }
    //获取StripedMap对象,一般都使用这个函数获取StripedMap对象,
    //从而操作引用计数和所引用计数
    static StripedMap<SideTable>& SideTables() {
        return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
    }
    
    

    三、weak_table_t
    用来保存弱引用,是个结构体,里面有4个属性,

    struct weak_table_t {
        weak_entry_t *weak_entries;//保存每个对象的弱引用的数组,
        size_t    num_entries;//数组的元素个数
        uintptr_t mask;//weak_entries数组的长度-1,不是数组元素个数-1;因为数组的长度可能是100,但是元素个数可能是9个;
        这个属性另一个作用是参与到哈希运算,得到对象的对应的数组索引index;
        uintptr_t max_hash_displacement;最大冲突或者异常次数,如果运算过程中冲突或者异常次数超过这个值max_hash_displacement则有问题
    };
    

    四、weak_entry_t

    弱引用实体,一个对象对应一个weak_entry_t,保存对象的弱引用;
    保存弱应用的是个联合体,当弱引用小于等于4个的时候,直接用inline_referrers数组保存
    大于四个时用referrers动态数组

    typedef DisguisedPtr<objc_object *> weak_referrer_t;
    #if __LP64__
    #define PTR_MINUS_2 62
    #else
    #define PTR_MINUS_2 30
    #endif
    #define WEAK_INLINE_COUNT 4
    #define REFERRERS_OUT_OF_LINE 2
    
    struct weak_entry_t {
        DisguisedPtr<objc_object> referent;//被弱引用的对象
        //
        union {
            struct {
                weak_referrer_t *referrers;//动态数组保存弱引用的指针
                uintptr_t        out_of_line_ness : 2;//记录是否需要使用动态数组
                uintptr_t        num_refs : PTR_MINUS_2;//动态数组中的元素个数
                uintptr_t        mask;//动态数组的长度-1,作用同weak_table_t的mask
                uintptr_t        max_hash_displacement;//最大冲突或者异常次数,如果运算过程中冲突或者异常次数超过这个值max_hash_displacement则有问题
            };
            struct {
                //初始化时默认使用的数组
                weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
            };
        };
    
    //判断是否大于4个,师傅需要动态数组
        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;
        }
    //构造函数,里面默认使用inline_referrers数组,当数据大于4个时,会改成动态数组referrers
        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;
            }
        }
    };
    
    

    五、下面介绍的是对弱引用的操作函数,包括添加,删除弱引用
    下面是objc-weak.mm文件全部内容

    
    /*
     * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
     *
     * @APPLE_LICENSE_HEADER_START@
     * 
     * This file contains Original Code and/or Modifications of Original Code
     * as defined in and that are subject to the Apple Public Source License
     * Version 2.0 (the 'License'). You may not use this file except in
     * compliance with the License. Please obtain a copy of the License at
     * http://www.opensource.apple.com/apsl/ and read it before using this
     * file.
     * 
     * The Original Code and all software distributed under the License are
     * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
     * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
     * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
     * Please see the License for the specific language governing rights and
     * limitations under the License.
     * 
     * @APPLE_LICENSE_HEADER_END@
     */
    
    #include "objc-private.h"
    
    #include "objc-weak.h"
    
    #include <stdint.h>
    #include <stdbool.h>
    #include <sys/types.h>
    #include <libkern/OSAtomic.h>
    
    //获取weak_entry_t保存的弱引用个数,保存在mask中的时候会-1,现在取出来的时候再+1,就是弱引用数组的长度了
    #define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
    
    //提前声明函数,c语法
    static void append_referrer(weak_entry_t *entry, objc_object **new_referrer);
    
    //错误处理函数
    BREAKPOINT_FUNCTION(
        void objc_weak_error(void)
    );
    //错误处理函数
    static void bad_weak_table(weak_entry_t *entries)
    {
        _objc_fatal("bad weak table at %p. This may be a runtime bug or a "
                    "memory error somewhere else.", entries);
    }
    
    /** 
     * Unique hash function for object pointers only.
     * 
     * @param key The object pointer
     * 
     * @return Size unrestricted hash of pointer.
     */
     //哈希运算,把对象的指针进行哈希运算,用来计算哈希表的key
    static inline uintptr_t hash_pointer(objc_object *key) {
        return ptr_hash((uintptr_t)key);
    }
    
    /** 
     * Unique hash function for weak object pointers only.
     * 
     * @param key The weak object pointer. 
     * 
     * @return Size unrestricted hash of pointer.
     */
     //哈希运算,把对象的指针进行哈希运算,用来计算哈希表的key
    static inline uintptr_t w_hash_pointer(objc_object **key) {
        return ptr_hash((uintptr_t)key);
    }
    
    /** 
     * Grow the entry's hash table of referrers. Rehashes each
     * of the referrers.
     * 
     * @param entry Weak pointer hash set for a particular object.
     */
     //用来对weak_entry_t中的数组进行扩容,
     //如果数组元素大于4个,或者大于数组长度的3/4时,调用此函数进行扩容;
     //数组扩容后的长度是原来长度的1倍
    __attribute__((noinline, used))
    static void grow_refs_and_insert(weak_entry_t *entry, 
                                     objc_object **new_referrer)
    {
        assert(entry->out_of_line());
    
        size_t old_size = TABLE_SIZE(entry);//获取原来数组的长度
        size_t new_size = old_size ? old_size * 2 : 8;//进行扩容,长度变成原来的2倍
    
        size_t num_refs = entry->num_refs;//原来的弱引用个数
        weak_referrer_t *old_refs = entry->referrers;//获取原来的数组指针,暂时保存在old_refs中
        entry->mask = new_size - 1;//把新的长度-1保存在mask,;
        
        entry->referrers = (weak_referrer_t *)
            calloc(TABLE_SIZE(entry), sizeof(weak_referrer_t));//重新创建一块内存,用来存储弱引用指针,并设置给referrers属性
        entry->num_refs = 0;//元素个数设置0
        entry->max_hash_displacement = 0;//最大冲突数设置0
        
    
        for (size_t i = 0; i < old_size && num_refs > 0; i++) {
            if (old_refs[i] != nil) {
            //这里是把原来的弱引用按顺序保存到新创建的数组中
                append_referrer(entry, old_refs[i]);
                num_refs--;
            }
        }
        // Insert
        append_referrer(entry, new_referrer);把新的弱引用保存新的数组中
        if (old_refs) free(old_refs);//最后释放旧的弱引用内存
    }
    
    /** 
     * 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)
    {
        //先判断弱引用数组是否大于大于4个,如果不大于4,就走这里
        if (! entry->out_of_line()) {
    
            // Try to insert inline.
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
            //先判断数组中对应的元素是否为nil,如果是nil,说明弱引用的指针还不到4个,就直接保存,然后return;
            //如果都不是nil,说明默认的数组已经存了4个元素了,就进行下一步;
                if (entry->inline_referrers[i] == nil) {
                //直接保存到默认的固定长度为4的数组中
                    entry->inline_referrers[i] = new_referrer;
                    //返回
                    return;
                }
            }
    
            //到这里,说明弱引用的数组超过4个了,
            //从新申请一块内存,用来保存弱引用指针;
            weak_referrer_t *new_referrers = (weak_referrer_t *)
                calloc(WEAK_INLINE_COUNT, sizeof(weak_referrer_t));
            
            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;//最大异常次数0
        }
    
        assert(entry->out_of_line());
    
        //在这里判断是否需要扩容
        //如果元素个数大于数组长度的3/4时,就需要扩容
        if (entry->num_refs >= TABLE_SIZE(entry) * 3/4) {
        //调这个方法去扩容
            return grow_refs_and_insert(entry, new_referrer);
        }
        //通过哈希计算对象的指针对应的key,& (entry->mask)这个运算是为了防止计算的key超过数组的长度
        size_t begin = w_hash_pointer(new_referrer) & (entry->mask);
        size_t index = begin;//保存哈希后的key
        size_t hash_displacement = 0;//保存最大异常次数
        
        //这一步是计算,找到一个索引为index的位置上元素为nil的空位置,用来保存这个对象的弱引用指针
        while (entry->referrers[index] != nil) {
            //如果不等nil,说明有这个key,然后就hash_displacement+1,
            hash_displacement++;
            //然后在把key+1计算新的key,直到找到空的位置,否则下面抱错
            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++;
    }
    
    /** 
     * Remove old_referrer from set of referrers, if it's present.
     * Does not remove duplicates, because duplicates should not exist. 
     * 
     * @todo this is slow if old_referrer is not present. Is this ever the case? 
     *
     * @param entry The entry holding the referrers.
     * @param old_referrer The referrer to remove. 
     */
     //删除弱引用指针,分为从固定数组删除还是从动态数组删除
    static void remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
    {
    	//如果弱引用指针的个数小于等于4个,直接从固定数组中删除
        if (! entry->out_of_line()) {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i] == old_referrer) {
                //在这里找到指针,直接设置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;
        }
    //如果大于4,就需要从动态数组删除
    
    	//哈希计算出对象的key
        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;
            }
        }
        //找到之后,直接设置nil,并把num_refs减1,说明元素个数少一个
        entry->referrers[index] = nil;
        entry->num_refs--;
    }
    
    /** 
     * Add new_entry to the object's table of weak references.
     * Does not check whether the referent is already in the table.
     */
     //当一个对象第一次被弱引用时,需要把把生成的weak_entry_t保存在weak_table_t表中
    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);
    
    	//哈希referent计算出对象的key,& (weak_table->mask)是为了防止key超出弱引用数组的长度
        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;
        }
    }
    
    //修改弱引用表的大小,因为多出来的用不到表,太占内存,可以清除掉不用的表
    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;//暂时保存旧的数组
        weak_entry_t *new_entries = (weak_entry_t *)
            calloc(new_size, sizeof(weak_entry_t));//根据新的长度,创建新的数组
    
        weak_table->mask = new_size - 1;//保存数组长度,保存时进行了-1,获取时需要+1;
        weak_table->weak_entries = new_entries;//保存新的数组
        weak_table->max_hash_displacement = 0;
        weak_table->num_entries = 0;  // restored by weak_entry_insert below
        
        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_entry_insert(weak_table, entry);
                }
            }
            //最后释放旧数组
            free(old_entries);
        }
    }
    
    // Grow the given zone's table of weak references if it is full.
    //对弱引用表进行扩容
    static void weak_grow_maybe(weak_table_t *weak_table)
    {
        size_t old_size = TABLE_SIZE(weak_table);
    
        // Grow if at least 3/4 full.
        //如果弱引用表中的元素个数大于3/4时,就扩容一倍,
        if (weak_table->num_entries >= old_size * 3 / 4) {
        //调这个方法扩容
            weak_resize(weak_table, old_size ? old_size*2 : 64);
        }
    }
    
    // Shrink the table if it is mostly empty.
    //对弱引用表进行缩减,删除空的表
    static void weak_compact_maybe(weak_table_t *weak_table)
    {
        size_t old_size = TABLE_SIZE(weak_table);
    
        // Shrink if larger than 1024 buckets and at most 1/16 full.
        //如果弱引用表的长度大于1024,并且元素只有不到1/16,就进行缩减,把空表清除
        if (old_size >= 1024  && old_size / 16 >= weak_table->num_entries) {
            weak_resize(weak_table, old_size / 8);
            // leaves new table no more than 1/2 full
        }
    }
    
    
    /**
     * Remove entry from the zone's table of weak references.
     */
     //删除表,当一个对象释放时,没有弱引用了,就把这个表删掉
    static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
    {
        // remove entry
        //如果弱引用小于4,直接释放
        if (entry->out_of_line()) free(entry->referrers);
        bzero(entry, sizeof(*entry));//清空数据
    
        weak_table->num_entries--;//减1
        //判断是否需要缩减表
        weak_compact_maybe(weak_table);
    }
    
    
    /** 
     * Return the weak reference table entry for the given referent. 
     * If there is no entry for referent, return NULL. 
     * Performs a lookup.
     *
     * @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_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;
        //通过循环找到key
        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];
    }
    
    /** 
     * Unregister an already-registered weak reference.
     * This is used when referrer's storage is about to go away, but referent
     * isn't dead yet. (Otherwise, zeroing referrer later would be a
     * bad memory access.)
     * Does nothing if referent/referrer is not a currently active weak reference.
     * Does not zero referrer.
     * 
     * FIXME currently requires old referent value to be passed in (lame)
     * FIXME unregistration should be automatic if referrer is collected
     * 
     * @param weak_table The global weak table.
     * @param referent The object.
     * @param referrer The weak reference.
     */
     //注销这个对象的弱引用表
    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.
    }
    
    /** 
     * Registers a new (object, weak pointer) pair. Creates a new weak
     * object entry if it does not exist.
     * 
     * @param weak_table The global weak table.
     * @param referent The object pointed to by the weak reference.
     * @param referrer The weak pointer address.
     */
     //给一个对象添加新的弱引用指针
    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;
        objc_object **referrer = (objc_object **)referrer_id;
    
        //前面这些判断是不是一个OC对象
        if (!referent  ||  referent->isTaggedPointer()) return referent_id;
    
        // 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));
            } else {
                return nil;
            }
        }
    
        // now remember it and where it is being stored
        weak_entry_t *entry;
        //判断这个对象先前有没用弱引用表,如果有直接使用
        if ((entry = weak_entry_for_referent(weak_table, referent))) {
            append_referrer(entry, referrer);
        } 
        else {
        //没有弱引用表,就创建一个
            weak_entry_t new_entry(referent, referrer);
            weak_grow_maybe(weak_table);
            weak_entry_insert(weak_table, &new_entry);
        }
    
        // Do not set *referrer. objc_storeWeak() requires that the 
        // value not change.
    
        return referent_id;
    }
    
    
    #if DEBUG
    bool
    weak_is_registered_no_lock(weak_table_t *weak_table, id referent_id) 
    {
        return weak_entry_for_referent(weak_table, (objc_object *)referent_id);
    }
    #endif
    
    
    /** 
     * Called by dealloc; nils out all weak pointers that point to the 
     * provided object so that they can no longer be used.
     * 
     * @param weak_table 
     * @param referent The object being deallocated. 
     */
     //清空一个对象的所有弱引用指针,对象释放的时候会调用这个方法
    void 
    weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
    {
        objc_object *referent = (objc_object *)referent_id;
    
        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 %p\n", 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;
        }
        
        for (size_t i = 0; i < count; ++i) {
            objc_object **referrer = referrers[i];
            if (referrer) {
            //通过循环,把弱引用指针设置为nil
                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);
    }
    
    展开全文
  • Although the beam shifts of Gaussian beams have been measured by the weak measurement technique, the weak measurement for orbital angular momentum (OAM)-induced spatial shifts of vortex beams is ...
  • 永磁同步电机弱磁控制仿真模型
  • Weak-Password.zip

    2020-07-14 14:50:42
    压缩包包含了常见的弱口令用户名,弱口令密码。包含常见的服务ftp、telnet、mysql等。还有sql注入测试代码。
  • 逆序超平面排列与弱布吕阿序,范久瑜,,对每个排列$w$,我们可以根据$w$的逆序构造一个超平面排列$mathcal{A}_w$,称为$w$对应的逆序超平面排列。$mathcal{A}_w$中区域的个数小于等于在
  • Server-Aided Multi-Secret Sharing Scheme for Weak Computational Devices
  • Attentive Recurrent Neural Network Weak-supervised Multi-label Image Classification
  • 弱收敛余经验过程是概率统计专业博士生的必修课程,也是经典书籍。
  • weak_classdump, Cycript实时classdump加密二进制文件的替代方案 weak_classdump生成传递给函数的类的头文件的Cycript脚本。当你不能 classdump,当二进制文件被加密时,最有用的。用法示例:root# cycript -p Skype...
  • 自适应对象检测的Rebuild_Strong-Weak-Distribution-Alignment 这是个人论文的重建<用于自适应对象检测的强弱分布对齐>
  • 弱引力假设和全息原理,孙成一,,基于弱引力假设提出的一个限制条件在暴涨模型和精致模型中是成立的。但是该限制条件却与全息暗能量模型不符。在本文,我们将首先�
  • Rust: Box 智能指针进阶 - Rc、RefCell、Weak 文章目录Rust: Box 智能指针进阶 - Rc、RefCell、Weak正文1. Box 类型复习2. 自定义模拟 Box 类型3. 递归类型定义4. Box 进阶:Rc5. Box 进阶:RefCell6. 循环依赖 &...

    Rust: Box 智能指针进阶 - Rc、RefCell、Weak

    正文

    1. Box 类型复习

    Rust 内置类型: Box、Option、Result

    • Box 为一种特别的引用类型,实际数据存储在堆上
    • Box 作为引用类型默认实现了 Deref 特性来实现数据访问(获取真实数据引用)

    2. 自定义模拟 Box 类型

    我们自定义实现 Box 类型有两个目标

    • 作为智能指针管理真实数据的生命周期
    • 实现 Deref 特性代理真实数据的引用

    下面直接看代码

    • /src/own_smart_pointer.rs

    首先定义一个 MyBox 类型,并定义 new 方法用于构造

    use std::ops::Deref;
    
    struct MyBox<T>(T);
    
    impl<T> MyBox<T> {
        fn new(x: T) -> MyBox<T> {
            MyBox(x)
        }
    }
    

    接下来实现 Deref 特性,会在引用 Box 数据的时候代理成内部数据对象的引用

    impl<T> Deref for MyBox<T> {
        type Target = T;
    
        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }
    

    测试的时候我们可以看到,我们调用 hello 并传入 &name,Rust 会自动调用 deref 方法将 &name 转换成 &String 类型,然后再透过 String 的 Deref 实现转换成 &str 类型,最终才能够成功被调用

    fn hello(name: &str) {
        println!("Hello {}", name);
    }
    
    pub fn test() {
        println!(">>>>> test own_smart_pointer");
    
        let name = MyBox::new(String::from("superfree"));
        hello(&name);
    
        println!();
    }
    
    • 输出
    >>>>> test own_smart_pointer
    Hello superfree
    

    3. 递归类型定义

    然而上面的自定义 Box 类型有一个致命的缺陷:数据依旧保存在栈上

    标准库自带的 Box 类型会将真实数据存入堆,不过由于 Rust 的引用跟踪非常好几乎不会产生内存泄漏,所以大多数时候我们会基于以下几个条件来判断是否需要用到 Box 类型来保存数据

    • 占用大空间的数据对象:避免方法栈替换时的数据传输开销
    • 递归类型定义:编译时无法确定数据大小,则以固定大小的栈指针代替

    接下来我们来看看如何定义一个递归类型

    首先我们先来看错误的例子

    enum List {
        Cons(i32, List),
        Nil,
    }
    

    这样定义的一个问题在于,enum 会参照最大实例的对象划分空间,但是 Cons 将能够无限堆叠,变成一个占用无限空间的数据对象,因此我们需要将 Cons 的第二个数据改成一个固定大小的指针

    #[derive(Debug)]
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }
    

    这样一来 Rust 就能确定 Cons 是一个 i32 + 一个 Box 引用,而第二个数据指向的对象是 Cons 还是 Nil,则是保存在堆上与栈空间的分配无关

    • 完整例子:/src/recursive_type.rs

    下面我们定义两种类型:List、Node 都是属于包含自身类型的递归定义类型,因此需要使用 Box 包装来保证编译时的大小确定

    use crate::recursive_type::List::{Cons, Nil};
    
    #[derive(Debug)]
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }
    
    #[derive(Debug)]
    struct Node {
        val: i32,
        next: Box<Option<Node>>,
    }
    
    pub fn test() {
        println!(">>>>> test recursive_type");
    
        let list = Cons(3, Box::new(Cons(2, Box::new(Nil))));
        println!("list = {:?}", list);
    
        let mut node = Node {
            val: 1,
            next: Box::new(Some(Node {
                val: 2,
                next: Box::new(None),
            })),
        };
        println!("node = {:?}", node);
        *node.next = None;
        println!("node = {:?}", node);
    
        println!();
    }
    

    倒数第二句复习了一下 Box 的数据修改方法,使用 * 标识符并赋值新的数据对象

    • 输出
    >>>>> test recursive_type
    list = Cons(3, Cons(2, Nil))
    node = Node { val: 1, next: Some(Node { val: 2, next: None }) }
    node = Node { val: 1, next: None }
    

    接下来我们要来介绍几个更进阶的 Box 类型,他们都跟 Box 一样将数据保存在堆上,不过提供了更高级的特性

    4. Box 进阶:Rc

    第一种我们介绍的是 Rc<T> 类型。

    我们知道 Rust 定义了一种特别的 Ownership 数据所有权的概念,也就是一个数据只能有一个人掌握,当我们想借用数据的时候就要用 &、&mut 等方法创建引用

    然而大多时候 & 更多的是用在方法调用等暂时的借用上,当我们想让多个数据共同拥有一个数据项,也就是创建多个 Ownership 的时候就要用上 Rc<T> 类型

    基于上面的 List 类型,想象我们有下面这样一个链表场景

    这时候节点的存在并不依赖于一个 a,而是只要 a、b、c 三个有任何一个存活,节点 3 都应该继续存在,也就是三个指针同时共享了这个节点

    Rc<T> 类型做的就是自动记录这个引用的数量,知道引用归 0 的时候才将数据对象删除,创建引用的时候推荐使用 Rc::clone(&a) 的写法

    • /src/reference_count.rs

    下面我们把前面的 List 数据类型从 Box 改成使用 Rc 来记录引用数量

    use std::rc::Rc;
    use crate::reference_count::List::{Cons, Nil};
    
    #[derive(Debug)]
    enum List {
        Cons(i32, Rc<List>),
        Nil,
    }
    

    接下来模拟上面那张图的引用方式,然后使用 Rc::strong_count 来检查当前引用数量

    pub fn test() {
        println!(">>>>> test reference_count");
    
        let a = Rc::new(Cons(3, Rc::new(Cons(5, Rc::new(Nil)))));
        println!("a = {:?}", a);
        println!("a strong_count = {}", Rc::strong_count(&a));
    
        {
            let b = Rc::new(Cons(1, Rc::clone(&a)));
            println!("b = {:?}", b);
            println!("a strong_count = {}", Rc::strong_count(&a));
            {
                let c = Rc::new(Cons(2, Rc::clone(&a)));
                println!("c = {:?}", c);
                println!("a strong_count = {}", Rc::strong_count(&a));
            }
            println!("! drop c");
            println!("a strong_count = {}", Rc::strong_count(&a));
        }
    
        println!();
    }
    
    • 输出
    >>>>> test reference_count
    a = Cons(3, Cons(5, Nil))
    a strong_count = 1
    b = Cons(1, Cons(3, Cons(5, Nil)))
    a strong_count = 2
    c = Cons(2, Cons(3, Cons(5, Nil)))
    a strong_count = 3
    ! drop c
    a strong_count = 2
    

    我们可以看到当 c 消失之后,a 的引用数量剩 2 了

    5. Box 进阶:RefCell

    第二种则是 RefCell,这个 RefCell 想要解决的问题是数据可变性的问题,由于 Rust 严格的所有权系统,使得我们想要修改数据的时候变得不那么方便,全部写成 mut 又显得格外丑陋,不符合 Rust 对于数据修改性最小化的理念

    因此诞生出了一个 RefCell 类型,这样我们使用它的时候就好像在用普通的引用类型一样,并在必要的时候可以使用 borrow_mut 来创建一个可变引用。

    这样一来并不是说 Rust 放弃了对于所有权的管理,而是将可变/不可变引用的检查从编译时改编为运行时检查

    • /src/reference_cell.rs

    官方手册给出的例子是在 Mock 测试的时候,我们需要在预备阶段手动进行数据赋值,但是测试过程又希望将数据作为不可变引用来使用

    首先我们先定义一个接口,描述一个 Messenger 的行为

    trait Messenger {
        fn send(&self, message: &str);
    }
    

    接下来我们定义一个 Mock 类型作为桩

    #[derive(Debug)]
    struct MockMessenger {
        send_messages: RefCell<Vec<String>>,
    }
    
    impl MockMessenger {
        fn new() -> MockMessenger {
            MockMessenger {
                send_messages: RefCell::new(vec![])
            }
        }
    }
    
    impl Messenger for MockMessenger {
        fn send(&self, message: &str) {
            let mut mut_sm = self.send_messages.borrow_mut();
            mut_sm.push(String::from(message));
            drop(mut_sm);
            println!("receive message = {:?}", self.send_messages.borrow());
        }
    }
    

    这里我们看到 send_messages 属性是一个 RefCell 类型,就允许我们在运行时动态的去借用可变引用,来修改内部数据

    pub fn test() {
        println!(">>>>> test reference_cell");
    
        let mock_messenger = MockMessenger::new();
        mock_messenger.send("Hello");
        mock_messenger.send("World");
        println!("mock_messenger = {:?}", mock_messenger);
        println!("mock_messenger len = {}", mock_messenger.send_messages.borrow().len());
    
        println!();
    }
    
    • 输出
    >>>>> test reference_cell
    receive message = ["Hello"]
    receive message = ["Hello", "World"]
    mock_messenger = MockMessenger { send_messages: RefCell { value: ["Hello", "World"] } }
    mock_messenger len = 2
    

    最后我们可以看到数据成功的写入,但是又保持了 send_messages 属性的不可变性

    不过从另一个角度来说,RefCell 会造成我们需要主动的去维护 borrow_mut 产生的可变引用,Rust 不再会在运行时为我们检查(可以将上面的 drop(mut_sm); 取消注释就能够看到产生了运行时错误)

    6. 循环依赖 & Weak

    有了 RefCell 之后我们是有可能动态的去改变不可变引用的数据并同时能避过编译器的检查,直到运行时抛出异常

    这就产生了循环依赖的可能

    • /src/reference_cycles.rs

    首先我们先定义一个 List 类型,并赋予 tail 方法返回 Cons 类型的第二个数据引用

    use std::cell::RefCell;
    use std::rc::Rc;
    use crate::reference_cycles::List::{Cons, Nil};
    
    #[derive(Debug)]
    enum List {
        Cons(i32, RefCell<Rc<List>>),
        Nil,
    }
    
    impl List {
        fn tail(&self) -> Option<&RefCell<Rc<List>>> {
            match self {
                Cons(_, item) => Some(item),
                Nil => None
            }
        }
    }
    
    impl Drop for List {
        fn drop(&mut self) {
            println!("drop {:?}", self);
        }
    }
    

    然而第二个数据类型是 RefCell,允许我们动态修改内部数据

    pub fn test() {
        println!(">>>>> test reference_cycles");
    
        let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
        println!("reference a = {:?}", a);
        println!("reference a count = {}", Rc::strong_count(&a));
        println!("reference a next = {:?}", a.tail());
    
        let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
        println!("reference b = {:?}", b);
        println!("reference b count = {}", Rc::strong_count(&b));
        println!("reference b next = {:?}", b.tail());
    
        if let Some(link) = a.tail() {
            *link.borrow_mut() = Rc::clone(&b);
        }
    
        println!("reference a count = {}", Rc::strong_count(&a));
        println!("reference b count = {}", Rc::strong_count(&b));
    
        println!();
    }
    

    如此一来,在程序结束的时候 a、b 引用都消失了,但是两个对象互相持有对方的引用,因此内存并不会被回收

    • 输出
    >>>>> test reference_cycles
    reference a = Cons(5, RefCell { value: Nil })
    reference a count = 1
    reference a next = Some(RefCell { value: Nil })
    reference b = Cons(10, RefCell { value: Cons(5, RefCell { value: Nil }) })
    reference b count = 1
    reference b next = Some(RefCell { value: Cons(5, RefCell { value: Nil }) })
    drop Nil
    reference a count = 2
    reference b count = 2
    

    要想解决这个问题我们可以使用另一种引用类型:Weak<T> 弱引用类型。

    Rust 提供了另一种与 Rc 对应的弱引用类型 Weak,记录引用数量为 weak_count,不同的是真实的数据对象只关心 strong_count 的数量并在 strong_count 归零的时候直接回收。

    也就是每次调用 Weak 类型之前,需要调用 upgrade 来获取最新的引用对象

    • /src/reference_cycles_prevent.rs
    use std::cell::RefCell;
    use std::rc::{Rc, Weak};
    
    #[derive(Debug)]
    struct Node {
        val: i32,
        parent: RefCell<Weak<Node>>,
        children: RefCell<Vec<Rc<Node>>>,
    }
    
    pub fn test() {
        println!(">>>>> test reference_cycles_prevent");
    
        let leaf = Rc::new(Node {
            val: 3,
            parent: RefCell::new(Weak::new()),
            children: RefCell::new(vec![]),
        });
    
        println!("! create leaf");
        println!("leaf = {:?}", leaf);
        println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
        println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
        {
            let branch = Rc::new(Node {
                val: 5,
                parent: RefCell::new(Weak::new()),
                children: RefCell::new(vec![Rc::clone(&leaf)]),
            });
    
            println!("! create branch");
            println!("branch = {:?}", branch);
            println!("branch parent = {:?}", branch.parent.borrow().upgrade());
            println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
            println!("branch strong = {}, weak = {}", Rc::strong_count(&branch), Rc::weak_count(&branch));
    
            *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
    
            println!("! ref branch");
            println!("leaf = {:?}", leaf);
            println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
            println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
            println!("branch strong = {}, weak = {}", Rc::strong_count(&branch), Rc::weak_count(&branch));
        }
        println!("! drop branch");
        println!("leaf = {:?}", leaf);
        println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
        println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
    
        println!();
    }
    
    • 输出
    >>>>> test reference_cycles_prevent
    ! create leaf
    leaf = Node { val: 3, parent: RefCell { value: (Weak) }, children: RefCell { value: [] } }
    leaf parent = None
    leaf strong = 1, weak = 0
    ! create branch
    branch = Node { val: 5, parent: RefCell { value: (Weak) }, children: RefCell { value: [Node { val: 3, parent: RefCell { value: (Weak) }, children: RefCell { value: [] } }] } }
    branch parent = None
    leaf strong = 2, weak = 0
    branch strong = 1, weak = 0
    ! ref branch
    leaf = Node { val: 3, parent: RefCell { value: (Weak) }, children: RefCell { value: [] } }
    leaf parent = Some(Node { val: 5, parent: RefCell { value: (Weak) }, children: RefCell { value: [Node { val: 3, parent: RefCell { value: (Weak) }, children: RefCell { value: [] } }] } })
    leaf strong = 2, weak = 0
    branch strong = 1, weak = 1
    ! drop branch
    leaf = Node { val: 3, parent: RefCell { value: (Weak) }, children: RefCell { value: [] } }
    leaf parent = None
    leaf strong = 1, weak = 0
    

    上面的程序建立了一个父子节点关系

    理论上按照前面的经验两个节点互相持有对方的引用应该也是一个内存泄漏,但是由于 parent 指针持有的是弱引用,因此在 branch 变量回收的时候 strong_count 变成 0 就会被回收了,同时 leaf 也会一并被回收了。

    7. 结论

    在 Rust 的理念里面,我们需要全面按照 Rust 的所有权规则来进行数据的操作、访问、共享。

    对于堆上数据我们则需要使用 Box、Rc、Weak、RefCell 等类型,并使用如 borrowborrow_mut 方法来获取引用指针等

    使用上还是比较难懂,需要多练习hh。

    其他资源

    参考连接

    TitleLink
    Smart Pointers - The Rust Programming Languagehttps://doc.rust-lang.org/book/ch15-00-smart-pointers.html

    完整代码示例

    https://github.com/superfreeeee/Blog-code/tree/main/back_end/rust/rust_box_deep_dive

    展开全文
  • 导航书籍,GNSS相关信号处理,英文版,《GNSS receiver for weak signal》

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 130,374
精华内容 52,149
关键字:

weak

友情链接: Android-UI-design.rar