Android智能指针

前言

Java 和 C/C++ 的一个重大区别,就是它没有"指针"的概念,这并不代表 Java 不需要只用指针,而是将这个"超级武器隐藏了"。Java 以其他更"安全"的形式向开发人员提供了隐形的"指针",使得用户既能享受到指针的强大功能,又能尽量避免指针带来的问题。

C/C++中常见的指针问题

  1. 指针没有初始化

对指针进行初始化是程序员必须养成的良好习惯,也是指针问题中最容易解决和控制的一个问题;

  1. new 了对象没有及时 delete

动态分配内存的对象,其实声明周期的控制不当常常会引起不少麻烦。如果只有一个程序员在维护时,问题通常不大,因为只要稍微留心就可以实现 new 和 delete 的配套操作;但是如果一个大型工程,就很可能会出现动态分配的内存没有回收的情况——造成的内存泄露问题往往是致命的;

  1. 野指针
  • 假设1:我们 new 了一个对象 A,并将指针 ptr 指向这个新的对象。当对 A 使用结束后,我们也主动 delete 了 A,但是唯一没做的是将 ptr 指针置空,那么可能出现野指针问题。因此如果有"第三方"视图用 ptr 来使用内存对象,它首先通过判断发现 ptr 不为空,就认为这个对象还是存在的,其结果就是导致程序崩溃或是数据错误;

  • 假设2:假设 ptr1 和 ptr2 都指向对象 A,后来我们通过 ptr1 释放了 A 的内存空间,并且将 ptr1 也置为 null;但是 ptr2 并不知道它所指向的内存对象已经不存在了,此时如果 ptr2 来访问 A 也会导错误;

Android 智能指针

在开发中经常会使用对象引用计数来维护对象的生命周期,而该技术的核心问题是由谁来维护对象的引用计数,由开发人员维护显然既不可靠,又不方便编写和维护。而智能指针正是一种可以自动维护对象引用计数的计数,需要注意的是,智能指针是一个对象,而不是一个指针

现在考虑这样一个问题:

有两个对象 A 和 B,A 引用了 B,同时 B 也引用了 A。当对象 A 不再使用时,就可以释放它所占用的内存,但是由于 B 还持有 A 的引用,结果就是 A 不能被释放。对于释放 B 的资源时也会遇到
同样的问题而不能得到释放。这个问题也是垃圾回收系统所遇到的经典问题之一,因为它一次只能收集一个对象占用的内存(还要看的具体的回收机制)。

这就要使用一种特殊的智能指针技术,该技术将对象的引用计数分为强引用计数和弱引用计数两种,其中对象生命周期只受强引用计数控制。在使用以上引用计数方式时,一般将有关联的对象划分为“父——子”和“子——父”关系。“父”对象通过强引用来引用“子”对象,“子”对象通过弱引用来引用“父”对象。

以上面的 A 和 B 对象为例,假设 A 和 B 是“父——子”关系,对象 A 通过强引用来引用 B,而 B 通过弱引用来引用 A。当对象 A 不再使用的时,由于 B 使用过弱引用来引用 A 的,而 对象的生命周期只受强引用计数控制,所以,A 的生命周期不受 B 的影响,可以安全释放。在释放 A 的同时,也会释放它对 B 的强引用计数,因此 B 再不需要时可以被安全释放。

由于对象生命周期只受强引用计数控制,因此在 B 想要使用 A 时,A 可能已经被释放了,这个时候 B 不能直接使用 A 对象,而是先要成功的将对象 A 的弱引用计数升级为强引用计数,然后才能使用 A;如果引用计数升级失败,那么 B 就无法使用 A 了。

Android 系统设计了三种类型的 C++ 智能指针,分别为:

  1. 轻量级指针:Light Pointer
  2. 强指针:Strong Pointer
  3. 弱指针:Weak set_pointer

其中轻量级指针使用了简单引用计数,而强指针和弱指针使用了强引用计数和引用计数。

Android 提供了基类 RefBase,用以管理引用数,所有支持使用强指针和弱指针的类必须从 RefBase 派生。设计模板类 sp、wp,用以引用实际对象,sp、wp 声明为栈对象,作用域结束时,自动释放,自动调用机析构函数。因此可以在 sp、wp 的构造函数中,增加引用计数,在析构函数中,减少引用计数。专门设计的 weakref_impl 类,该类是 RefBase 的内部类,用来做真正的引用数管理,都由 mRef 来管理。

Android智能指针的关系图:

Android智能指针的源码位置

android中的智能指针的主要代码是:RefBase.h、RefBase.cpp 以及 Pointer.h 这三个文件,他们分别位于:

RefBase.cpp: /system/core/libutils/RefBase.cpp
RefBase.h: /system/core/include/utils/RefBase.h
StrongPointer.h:/system/core/include/utils/StrongPointer.h

轻量级指针

这里不多提轻量级指针,因为这种指针式通过简单引用计数技术来维护对象生命周期的。关于它只需知道一下 3 点:

  1. 第一点使用它需要继承 LightRefBase(模板类);

     public LightClass: public LightRefBase<LightClass>
    
  2. 第二点 LightRefBase 类只有一个成员变量 mCount 用来描述一个对象的引用计数值;

  3. 第三点需要知道轻量级指针的实现类和强指针的实现类是同一个类 sp。

强指针和弱指针

强指针和弱指针通过强引用计数器和弱引用计数器来维护对象的生命周期。如果一个类的对象要使用强指针和弱指针,那么就必须从 RefBase 类继承下来,因为 RefBase 类提供了强引用和弱引用计数器。

强指针和弱指针关系比较密切,他们是配合在一起使用的。

强指针的实现原理分析

首先分析 RefBase 类的实现原理,源码如下:

源码位置:Android源码目录/system/core/include/utils/RefBase.h

class RefBase
{
public:
            void            incStrong(const void* id) const; // 增加强引用计数器的值
            void            decStrong(const void* id) const; // 减少强引用计数器的值

            void            forceIncStrong(const void* id) const;

            //! DEBUGGING ONLY: Get current strong ref count.
            int32_t         getStrongCount() const;

    class weakref_type
    {
    public:
        RefBase*            refBase() const;

        void                incWeak(const void* id); //增加弱引用计数器的值
        void                decWeak(const void* id); //减少弱引用计数器的值

        // acquires a strong reference if there is already one.
        bool                attemptIncStrong(const void* id); //增加强引用计数器的值

        // acquires a weak reference if there is already one.
        // This is not always safe. see ProcessState.cpp and BpBinder.cpp
        // for proper use.
        bool                attemptIncWeak(const void* id); //减少强引用计数器的值

        //! DEBUGGING ONLY: Get current weak ref count.
        int32_t             getWeakCount() const;

        //! DEBUGGING ONLY: Print references held on object.
        void                printRefs() const;

        //! DEBUGGING ONLY: Enable tracking for this object.
        // enable -- enable/disable tracking
        // retain -- when tracking is enable, if true, then we save a stack trace
        //           for each reference and dereference; when retain == false, we
        //           match up references and dereferences and keep only the
        //           outstanding ones.

        void                trackMe(bool enable, bool retain);
    };

            weakref_type*   createWeak(const void* id) const;

            weakref_type*   getWeakRefs() const;

            //! DEBUGGING ONLY: Print references held on object.
    inline  void            printRefs() const { getWeakRefs()->printRefs(); }

            //! DEBUGGING ONLY: Enable tracking of object.
    inline  void            trackMe(bool enable, bool retain)
    {
        getWeakRefs()->trackMe(enable, retain);
    }

    typedef RefBase basetype;

protected:
                            RefBase(); // 构造函数
    virtual                 ~RefBase(); // 析构函数

    //! Flags for extendObjectLifetime()
    enum {
        OBJECT_LIFETIME_STRONG  = 0x0000,
        OBJECT_LIFETIME_WEAK    = 0x0001,
        OBJECT_LIFETIME_MASK    = 0x0001
    };

            void            extendObjectLifetime(int32_t mode);

    //! Flags for onIncStrongAttempted()
    enum {
        FIRST_INC_STRONG = 0x0001
    };

    virtual void            onFirstRef();
    virtual void            onLastStrongRef(const void* id);
    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
    virtual void            onLastWeakRef(const void* id);

private:
    friend class weakref_type;
    class weakref_impl;

                            RefBase(const RefBase& o);
            RefBase&        operator=(const RefBase& o);

private:
    friend class ReferenceMover;

    static void renameRefs(size_t n, const ReferenceRenamer& renamer);

    static void renameRefId(weakref_type* ref,
            const void* old_id, const void* new_id);

    static void renameRefId(RefBase* ref,
            const void* old_id, const void* new_id);

        weakref_impl* const mRefs; // 描述对象引用计数
};

RefBase 提供了成员函数 incStrong 和 decStrong 来维护他所引用的对象的引用计数,这是通过使用一个 weakref_impl 对象,即成员变量 mRefs 来描述对象的引用计数。

weakref_impl 同时为类提供了强引用和弱引用计数,源码如下:

源码位置:Android源码目录 /system/core/libutils/RefBase.cpp

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    std::atomic<int32_t>    mStrong; // 强引用计数
    std::atomic<int32_t>    mWeak; // 弱引用计数
    RefBase* const          mBase; // 引用对象的地址
    std::atomic<int32_t>    mFlags; // 描述生命周期控制方式

#if !DEBUG_REFS

    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
    {
    }

    void addStrongRef(const void* /*id*/) { }
    void removeStrongRef(const void* /*id*/) { }
    void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { }
    void addWeakRef(const void* /*id*/) { }
    void removeWeakRef(const void* /*id*/) { }
    void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { }
    void printRefs() const { }
    void trackMe(bool, bool) { }

#else
    ......
#endif
};

weakref_impl 继承了 weakref_type 类,weakref_type 为 RefBase 内部类,它提供了成员函数:incWeak、decWeak、attemptIncStrong 和 attemptIncWeak 来维护对象的
强引用计数和弱引用计数。weakref_type 只提供了方法接口,具体实现由 weakref_impl 完成。

weakref_impl 有两个成员变量 mStrong 和 mWeak,分别描述对象的强引用计数和弱引用计数。weakref_impl 的成员变量 mBase 指向了它所引用的对象的地址, 成员变量 mFlags 是一个标志值,用来描述对象的生命周期的控制方式。mFlags 的去值范围为:OBJECT_LIFETIME_STRONG、OBJECT_LIFETIME_WEAK 或是
OBJECT_LIFETIME_FOREVER,其中 OBJECT_LIFETIME_STRONG 表示对象的生命周期只受到强引用计数的影响;OBJECT_LIFETIME_WEAK 表示对象的生命周期同时受到强引用计数和弱引用计数的影响;OBJECT_LIFETIME_FOREVER 表示完全不受强引用计数和弱引用计数的影响。

以上三个类关系如下:

RefBase_reference_01.png

强指针实现类 sp

强指针的实现类为 sp,下面主要分析它的构造蛤蟆数和析构函数。

sp 源码如下:

源码位置:Android源码目录/system/core/include/utils/StrongPointer.h

template<typename T>
class sp {
public:
    inline sp() : m_ptr(0) { }

    sp(T* other);
    sp(const sp<T>& other);
    sp(sp<T>&& other);
    template<typename U> sp(U* other);
    template<typename U> sp(const sp<U>& other);
    template<typename U> sp(sp<U>&& other);

    ~sp();

    // Assignment

    sp& operator = (T* other);
    sp& operator = (const sp<T>& other);
    sp& operator = (sp<T>&& other);

    template<typename U> sp& operator = (const sp<U>& other);
    template<typename U> sp& operator = (sp<U>&& other);
    template<typename U> sp& operator = (U* other);

    //! Special optimization for use by ProcessState (and nobody else).
    void force_set(T* other);

    // Reset

    void clear();

    // Accessors

    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }

    // Operators

    COMPARE(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)

private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};

sp 构造函数

sp 的构造函数如下:

源码位置:Android源码目录/system/core/include/utils/StrongPointer.h

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}

模块参数 T 继承了 RefBase 类的子类,因此,以上代码实际上会调用 RefBase 的成员函数 incStrong 来增加对象的强引用计数,如下所示:

源码位置:Android源码目录/system/core/libutils/RefBase.cp

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id); // 增加对象的弱引用计数

    refs->addStrongRef(id);
    // 增加对象的强引用计数
    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
    ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
            std::memory_order_relaxed);
    // A decStrong() must still happen after us.
    ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
    // 来通知对象它被强指针引用了
    refs->mBase->onFirstRef();
}

RefBase 的成员变量 mRefs 是在构造函数中初始化的,如下所示:

RefBase::RefBase()
    : mRefs(new weakref_impl(this))
{
}

接着分析 RefBase 的 incStrong 函数,它主要做了三件事:

  1. 增加对象的弱引用计数;
  2. 增加对象的强引用计数;
  3. 如果对象是第一次被强指针引用,调用成员函数 onFirstRef 来通知对象,它被强指针引用了,以便可以执行一些业务逻辑。

增加对象弱引用计数是通过调用 RefBase 的成员变量 mRefs(也就是 weakref_impl)的成员函数 incWeak 来实现的,它是 weakref_type 的子类,函数 incWeak 是从其父类继承下来的,
weakref_type 中 incWeak 代码如下:

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id); // 调试相关,忽略
    const int32_t c __unused = impl->mWeak.fetch_add(1,
            std::memory_order_relaxed); // 增加弱引用计数
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

这里 this 指针实际上是指向一个 weakref_impl 对象,因此将其转化为 weakref_impl 指针,接下来增加它的成员变量 mWeak 的值,即增加对象的弱引用计数。

增加了对向的弱引用后,接着就增加对象的强引用计数,也就是增加 mRefs 的成员变量 mStrong 的值。并返回对象原来的强引用计数值,即加一前的值。在 weakref_impl 的构造函数中,成员
变量 mStrong 的值被初始化为 INITIAL_STRONG_VALUE。INITIAL_STRONG_VALUE 是一个宏,其定义如下:

#define INITIAL_STRONG_VALUE (1<<28)

理论上,对象第一次被强指针引用时,它的强引用计数应该为 1,但是 INITIAL_STRONG_VALUE + 1 的值并不等于 1,因此,RefBase 类的成员函数 incStrong 就需要将它的值调整为 1 这
是通过

int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,std::memory_order_relaxed);

实现的。

至此 sp 的构造函数分析完了,它主要做的事就是增加强引用计数和弱引用计数。

下分析 sp 的析构函数。

sp 析构函数

析构函数代码如下:

源码位置:Android源码目录/system/core/include/utils/StrongPointer.h

template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}

m_ptr 所指向的对象是继承了 RefBase 的类,所以这里实际上调用了 RefBase 的 decStrong 函数来减少对象的强引用计数,其实现如下:

源码位置:Android源码目录/system/core/libutils/RefBase.cpp

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
#if PRINT_REFS
    ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        refs->mBase->onLastStrongRef(id);
        int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }

    refs->decWeak(id);
}

sp 析构函数的主要工作就是减少对象强引用计数和弱引用计数。与其构造函数一样

const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);

也是返回原来的强引用计数,即减1前的值,并保存在了变量 c 中。如果变量 c 的值等于1,也就是说,此时没有强指针引用这个对象了,接下来就可以调用

refs->mBase->onLastStrongRef(id);

来执行一些业务相关逻辑,同时也需要考虑是否需要释放该对象。接下来判断对象的生命周期是否只受强引用控制,如果是,那么就下来就会释放对象所占用的内存,同时导致 RefBase 的
析构函数被调用,代码如下:

源码位置:Android源码目录/system/core/libutils/RefBase.cpp

RefBase::~RefBase()
{
    if (mRefs->mStrong.load(std::memory_order_relaxed)
            == INITIAL_STRONG_VALUE) {
        delete mRefs;
    } else {
        int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);
        // 生命周期不只是受强引用计数控制
        if ((flags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
            if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
                delete mRefs;
            }
        }
    }
    // 只受强引用计数控制,只释放 RefBase,保留 mRefs
    const_cast<weakref_impl*&>(mRefs) = NULL;
}

如果强引用计数为初始值,也就是说该对象没有被强指针引用过,那么释放成员变量 mRefs(weakref_impl);当强引用计数为 0,但是弱引用不为 0时,只能将对象 RefBase 释放掉,而不能将 weakref_impl
对象 mRefs 释放掉,因为还有其他的弱指针通过该 weakref_impl 对象来引用实际对象。只有对象的弱引用计数为 0 时,才可以将 weakref_impl 一起释放掉。

回到函数 decStrong 中,接下来通过

refs->decWeak(id);

减少弱引用计数。refs 是 weakref_impl 对象,weakref_impl继承自 weakref_type,这里实际会调用 weakref_type 的 decWeak 函数,代码如下:

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);
    // 减少弱引用计数
    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;
    atomic_thread_fence(std::memory_order_acquire);

    int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
    if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
        if (impl->mStrong.load(std::memory_order_relaxed)
                == INITIAL_STRONG_VALUE) {
            // Special case: we never had a strong reference, so we need to
            // destroy the object now.
            delete impl->mBase;
        } else {
            // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
            delete impl;
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        delete impl->mBase;
    }
}

函数

impl->mWeak.fetch_sub(1, std::memory_order_release);

会返回对象的原弱引用计数,即减1前的值,并存放在变量 c 中,如果 c 不为 1,也就是说还有其他弱指针指向该对象,因此就不做进一步处理;如果 c 的值等于 1,也就是说没有其他弱指针指引用
该对象了,同时也说明没有强指针引用该对象了,这是需要考虑是否释放掉该对象。这取决于对象生命周期的控制方式,以及该对象是否被强指针引用过。下面分两种情况讨论。

  1. 对象生命周期只受强引用计数控制,即 (flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG 为 true,如果此时 impl->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE 也为 true,也就是说该对象没有被强指针引用过,那么就可以将该对象释放掉。如果对象的生命周期只受强引用计数控制,并且也被强指针引用过,那么在该对象的弱引用计数变为 0 时,该对象就已经在 RefBase 的成员函数 decStrong 中被释放掉了,因此,接下来就只释放其内部的引用计数器对象 weakref_impl。

  2. 生命周期受弱引用控制,,那么接下来就可以调用 onLastWeakRef 来处理一些业务相关逻辑,接着将将该对象释放掉。

总结

  • 如果一个对象的生命周期只受到强引用控制,那么只要它的强引用计数值为 0,系统就会释放掉该对象;
  • 如果一个对象的生命周期控制标志被设置为 OBJECT_LIFETIME_WEAK,只有当强引用计数和弱引用计数都是 0时,系统才会释放掉这个对象。

弱指针的实现原理分析

wp 类的定义如下;

template <typename T>
class wp
{
public:
    typedef typename RefBase::weakref_type weakref_type;

    inline wp() : m_ptr(0) { }

    wp(T* other);
    wp(const wp<T>& other);
    wp(const sp<T>& other);
    template<typename U> wp(U* other);
    template<typename U> wp(const sp<U>& other);
    template<typename U> wp(const wp<U>& other);

    ~wp();

    // Assignment

    wp& operator = (T* other);
    wp& operator = (const wp<T>& other);
    wp& operator = (const sp<T>& other);

    template<typename U> wp& operator = (U* other);
    template<typename U> wp& operator = (const wp<U>& other);
    template<typename U> wp& operator = (const sp<U>& other);

    void set_object_and_refs(T* other, weakref_type* refs);

    // promotion to sp

    sp<T> promote() const;

    // Reset

    void clear();

    // Accessors

    inline  weakref_type* get_refs() const { return m_refs; }

    inline  T* unsafe_get() const { return m_ptr; }

    // Operators

    COMPARE_WEAK(==)
    COMPARE_WEAK(!=)
    COMPARE_WEAK(>)
    COMPARE_WEAK(<)
    COMPARE_WEAK(<=)
    COMPARE_WEAK(>=)

    inline bool operator == (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
    }
    template<typename U>
    inline bool operator == (const wp<U>& o) const {
        return m_ptr == o.m_ptr;
    }

    inline bool operator > (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
    }
    template<typename U>
    inline bool operator > (const wp<U>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
    }

    inline bool operator < (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
    }
    template<typename U>
    inline bool operator < (const wp<U>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
    }
                         inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
    template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
                         inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
    template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
                         inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
    template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }

private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;

    // 指向他所引用的对象(继承自 RefBase)
    T*              m_ptr;
    // 用来维护对象的弱引用计数
    weakref_type*   m_refs;
};

需要注意的是,弱指针与强指针有一个很大的区别,就是弱指针不可以直接操作他所引用的对象,因为他所引用的对象可能不受弱引用计数的控制,即它所引用的对象可能是一个无效的对象。因此如果需要操作一个弱指针引用的对象,那么就需要将这个弱指针升级为强指针,这是通过它的成员函数 promote 实现的。如果升级成功,那么就说明该弱指针所引用的对象还没有被销毁,可以正常使用。

wp 的实现比较复杂,重点是理解它的构造函数、析构函数意以及如何将一个弱指针升级为一个强指针。

首先分析它的构造函数。

wp 构造函数

源码位置:Android源码目录/system/core/include/utils/RefBase.h

template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) m_refs = other->createWeak(this);
}

实际上是调用 RefBase 的成员函数 createWeak 来增加对象的弱引用计数,代码如下:

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

RefBase 的成员变量 mRefs 指向 weakref_impl 对象,它的成员方法 incWeak 就是增加实际引用对象的弱引用计数,最后将 mRefs 返回。

wp 析构函数

源码位置:Android源码目录/system/core/include/utils/RefBase.h

template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this);
}

就是调用 它的成员变量 m_refs 的成员函数 decWeak 来减少弱引用计数。m_refs指向的是一个 weakref_impl 对象,因此这里实际上会调用 weakref_impl 的成员函数 decWeak 来减少对象的弱引用计数。

接下来重点分析 wp 的成员函数 promote,该函数用来将一个弱指针升级为一个强指针。前面介绍到弱引用不能直接操作它引用的对象,那么这是如何实现的呢?

  • wp 没有重载 “*” 和 “->”操作符,因此,我们就不能直接操作它所引用的对象。

wp 的成员函数 promote 实现如下:

template<typename T>
sp<T> wp<T>::promote() const
{
    sp<T> result;
    if (m_ptr && m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);
    }
    return result;
}

m_ptr 指向对象的地址,m_refs 指向该对象内部的一个弱引用计数器。只有在对象地址不为NULL的情况下,才会调用它内部的弱引用计数器对象的成员函数 attemptIncStrong 来试图增加该对象的强引用计数。如果能够成功增加强引用计数,那么就可以成功地把一个弱指针升级为一个强指针。m_refs是一个类型为 weakref_type 的指针,因此接下来就会调用 weakref_type 的成员函数attemptIncStrong,其实现如下:

bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
    incWeak(id);

    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);

    ALOG_ASSERT(curCount >= 0,
            "attemptIncStrong called on %p after underflow", this);

    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
        if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
                std::memory_order_relaxed)) {
            break;
        }
    }

    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
        int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            if (curCount <= 0) {
                decWeak(id);
                return false;
            }

            while (curCount > 0) {
                if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
                        std::memory_order_relaxed)) {
                    break;
                }
            }

            if (curCount <= 0) {
                decWeak(id);
                return false;
            }
        } else {
            if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
                decWeak(id);
                return false;
            }

            curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed);

            if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) {
                impl->mBase->onLastStrongRef(id);
            }
        }
    }

    impl->addStrongRef(id);

#if PRINT_REFS
    ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endif

    if (curCount == INITIAL_STRONG_VALUE) {
        impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
                std::memory_order_relaxed);
    }

    return true;
}

weakref_type 的成员函数 attemptIncStrong 试图增加目标对象的强引用计数器,但是可能会增加失败(目标对象已经被释放,或者该目标对象不允许使用强指针引用它)。

在增加强引用计数的同时也会增加弱引用计数,因此以上函数首先调用 incWeak 来增加弱引用计数。wp 的成员变量 m_refs 指向的是一个 weakref_impl 对象,接下来可以安全地将 this 指针
转化为 weakref_impl 指针,并保存在 impl 中。

一个弱指针引用的对象可能处于两种状态:

  1. 该对象正被其他强指针引用,因此它的强引用计数值大于 0,并且不等于 INITIAL_STRONG_VALUE;
  2. 该对象没用被任何强者针引用,即它的强引用计数值小于等于 0,或是等于 INITIAL_STRONG_VALUE。

先分析第一种情形,由于它的强引用计数值大于 0,也就是说这时候对象一定是存在的,因此可以安全的将弱指针升级为强指针,并将对象的强引用计数加 1。

while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
    // we're in the easy/common case of promoting a weak-reference
    // from an existing strong reference.
    if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,
            std::memory_order_relaxed)) {
        break;
    }
}

compare_exchange_weak 可以保证原子性,也会出现增加强引用计数失败的情况,在调用函数 compare_exchange_weak 前,其他线程正在修改 curCount 的值就会造成这种情况,通过 while 循环来重新执行该操作。

第二种情况比较复杂,此时对象可能存在也可能不存在,要进一步判断。

  1. 如果 (flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG 为 true,也就是说该对象生命周期只受强引用计数控制。

    • 如果 curCount <= 0 也就是说该对象不存在,那么减少之前增加的弱引用计数,并返回 false;
    • 如果 curCount > 0,实际上就是 curCount == INITIAL_STRONG_VALUE,此时由于该对象生命周期只受强引用计数控制,而此时该对象又没有被任何强指针引用过,那么它必然不会被释放,此时可以安全的将其升级为强指针。
  2. 如果该对象的生命周期受弱引用计数影响,那么就说明该对象肯定是存在的,因为现在正有一个弱指针在引用该对象。但是还要进一步调用函数 onIncStrongAttempted 来确认对象是否允许强指针引用它。如果为 true,那么就可以成功升级为强指针。

onIncStrongAttempted 实现如下:

bool RefBase::onIncStrongAttempted(uint32_t flags, const void* /*id*/)
{
    return (flags&FIRST_INC_STRONG) ? true : false;
}

RefBase 类的成员函数在参数 flags 为 FIRST_INC_STRONG 的情况下允许将一个指向只受弱引用计数影响生命周期的对象的弱指针升级为强指针。

到这里关于只能指针的相关内容的分析就基本完成了,希望对你有帮助。

参考文献:

Android 系统源代码情景分析

Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析

Android跨进程通信IPC之4——AndroidIPC基础2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345