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閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375