Android進程間通信之5——智能指針

Java和C/C++的一個重大區(qū)別越妈,就是它沒有"指針"的概念错沃,這并不代表Java不需要指針边败,而是將這個"超級武器隱藏了"。如果大家使用C/C++開發(fā)過一些大型項目捎废,就會知道一個比較頭疼的問題——指針異常笑窜。所以Java以其他更"安全"的形式向開發(fā)人員提供了隱形的"指針",使得用戶既能享受到指針的強大功能登疗,又能盡量避免指針帶來的問題排截。

(一)嫌蚤、C/C++中常見的指針問題

1、指針沒有初始化

對指針進行初始化是程序員必須養(yǎng)成的良好習慣断傲,也是指針問題中最容易解決和控制的一個問題

2脱吱、new了對象沒有及時delete

動態(tài)分配內(nèi)存的對象,其實生命周期的控制不當认罩,常常會引起不少麻煩箱蝠。如果只有一個程序員在維護時,問題通常不大垦垂,因為只要稍微留心就可以實現(xiàn)new和delete的配套操作宦搬;但是如果一個大型工程(特別是多滴協(xié)同研發(fā)的軟件項目),由于溝通的不及時或者人員素質(zhì)的參差不齊劫拗,就很可能會出現(xiàn)動態(tài)分配的內(nèi)存沒有回收的情況——造成的內(nèi)存泄露問題往往是致命的间校。

3、野指針

  • 假設(shè)1:我們new了一個對象A页慷,并將指針ptr指向這個新是對象(即ptr= new )憔足。當對A使用結(jié)束后,我們也主動delete了A酒繁,但是唯一沒做的是將ptr指針置空滓彰,那么可能出現(xiàn)什么問題?沒錯州袒,就是野指針揭绑。因此如果有"第三方"視圖用ptr來使用內(nèi)存對象,它首先通過判斷發(fā)現(xiàn)ptr不為空稳析,自然而然的就認為這個對象還是存在的洗做,其結(jié)果就是導致死機。
  • 假設(shè)2:假設(shè)ptr1和ptr2都指向?qū)ο驛彰居,后來我們通過ptr1釋放了A的內(nèi)存空間诚纸,并且將ptr1也置為null;但是ptr2并不知道它所指向的內(nèi)存對象已經(jīng)不存在了陈惰,此時如果ptr2來訪問A也會導致死機

(二)畦徘、我們設(shè)計的解決方案

上面分析了C/C++指針的問題。如果讓我們設(shè)計Android的智能指針抬闯,怎么做才能防止以上幾個問題井辆?解決方案思路如下:

  • 問題1的解決方案:這個簡單,只要讓指針在創(chuàng)建時設(shè)置為null即可
  • 問題2的解決方案:比較復雜溶握,既然是智能指針就為意味著它應該是一個"雷鋒"杯缺,盡可能自動的實現(xiàn)new和delete的相應工作,那什么時候應該delete一個內(nèi)存對象呢睡榆?肯定是"不需要的時候"(其實是個廢話)萍肆。那怎么來分別什么是"需要"和"不需要"袍榆?在C/C++中,我們一般認為:當一個指針指向一個object的時候塘揣,這個內(nèi)存對象就是"需要"的包雀,當這個指針解除了與內(nèi)存對象的關(guān)系,我們就認為這個內(nèi)存對象已經(jīng)"不需要"了亲铡。所以我們想到用一個布爾類型變量來保存即可才写。
  • 問題3的解決方案:問題又來了,當有兩個指針及兩個以上指針同時使用這個內(nèi)存怎么辦奖蔓,用布爾類型肯定是不行的赞草。所以我們要用一個計數(shù)器來記錄這個內(nèi)存對象"被需要"的個數(shù)即可,當這個計數(shù)器遞減到零時锭硼,就說明這個內(nèi)存對象應該"壽終正寢"了房资。這就是在很多領(lǐng)域了都得到廣泛應用的"引用計數(shù)"的概念蜕劝。如下圖


    引用計數(shù)

(三)檀头、Android智能指針的原理

重點強調(diào) :
智能指針是一個對象,而不是一個指針.

  • Android設(shè)計了基類RefBase岖沛,用以管理引用數(shù)暑始,所有類必須從RefBase派生,RefBase是所有對象的始祖婴削。
  • 設(shè)計模板類sp廊镜、wp,用以引用實際對象唉俗,sp強引用和wp弱引用嗤朴。sp、wp聲明為棧對象虫溜,作用域結(jié)束時雹姊,自動釋放,自動調(diào)用機析構(gòu)函數(shù)衡楞。因此可以在sp吱雏、wp的構(gòu)造函數(shù)中,增加引用計數(shù)瘾境,在析構(gòu)函數(shù)中歧杏,減少引用計數(shù)。
  • 專門設(shè)計weakref_impl類迷守,該類是RefBase的內(nèi)部類犬绒,用來做真正的引用數(shù)管理,都有mRef來管理

Android智能指針的關(guān)系圖:


Android智能指針的關(guān)系圖

(四)兑凿、Android智能指針的源碼位置

android中的智能指針的主要代碼是:RefBase.h和RefBase.cpp StrongPointer.h 這三個文件凯力,他們分別位于:

RefBase.cpp:Android源碼目錄 /system/core/libutils/RefBase.cp
RefBase.h:Android源碼目錄 /system/core/include/utils/RefBase.h
StrongPointer.h:Android源碼目錄/system/core/include/utils/StrongPointer.h

鏈接如下

(五)眨业、強指針sp

看到sp,很多人會以為是StrongPointer的縮寫沮协。與sp對應的是wp龄捡,我們將會在下一節(jié)講解。
先來看下源碼:

///system/core/include/utils/StrongPointer.h    58行
template<typename T>
class sp {
public:
    inline sp() : m_ptr(0) { }
   
    sp(T* other);   //常用的構(gòu)造函數(shù)
    sp(const sp<T>& other); 
    template<typename U> sp(U* other);
    template<typename U> sp(const sp<U>& other);

    ~sp();    //析構(gòu)函數(shù)

    // Assignment

    sp& operator = (T* other);   // 重載運算符"="
    sp& operator = (const sp<T>& other);

    template<typename U> sp& operator = (const 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類的設(shè)計和我們之前想象的基本一致聘殖,比如運算符的實現(xiàn)為:

//system/core/include/utils/StrongPointer.h    157行
template<typename T>
sp<T>& sp<T>::operator =(T* other) {
    if (other)
        other->incStrong(this);    //增加引用計數(shù)
    if (m_ptr)
        m_ptr->decStrong(this); // 減少引用計數(shù)
    m_ptr = other;
    return *this;
}

上面的diamante同時考慮了對一個智能指針重復賦值的情況。即當m_ptr不為空時行瑞,要先撤銷它之前指向的內(nèi)存對象奸腺,然后才能賦予其新值。另外為sp分配一個內(nèi)存對象血久,不一定要通過操作運算符(比如等號)突照,它的構(gòu)造函數(shù)也是可以的。比如下面這段代碼

//system/core/include/utils/StrongPointer.h    112行
template<typename T> 
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this); //因為是構(gòu)造函數(shù)氧吐,所以不同擔心mptr之前已經(jīng)賦值過
}

這時候m_ptr就不用先置為null讹蘑,可以直接指向目標對象。而析構(gòu)函數(shù)的做法和我們的預想也是一樣筑舅。

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

(六)座慰、弱指針wp

1、弱引用產(chǎn)生的背景:

在前面討論之智能指針的"設(shè)計理念"時翠拣,其實是以強指針為原型逐步還原出智能指針作者的"意圖'版仔,那么"弱指針"又由什么作用?

其實弱指針主要是為了解決一個問題?那是什么問題那误墓?有這么一種情況:父對象指向子對象child蛮粮,然后子對象又指向父對象,這就存在了虛幻引用的現(xiàn)象谜慌。比如有兩個class

struct Parent
{
    Child *myson;
}

struct Child
{
    Parent *myfather;
}

這樣就會產(chǎn)生上面的問題然想。如果不考慮智能指針,這樣的情況不會導致任何問題畦娄,但是在智能指針的場景下又沾,就要注意了,因為Parent指向了Child熙卡,所以Child的引用計數(shù)器不為零杖刷。同時又由于Child指向了Parent,所以Parent的引用器不會為零驳癌。這有點類似于Java中的死鎖了滑燃。因為內(nèi)存回收者返現(xiàn)兩者都是"被需要"的狀態(tài),當然不能釋放颓鲜,從而形成了惡性循環(huán)表窘。

為了解決上面這個問題典予,產(chǎn)生了"弱引用"。具體措施如下:

Parent使用強指針來引用Child乐严,而Child只使用弱引用來指向父Parent類瘤袖。雙方規(guī)定當強引用計數(shù)器為0時,不論弱引用是否為0昂验,都可以delete自己(Android系統(tǒng)中這個規(guī)定是可以調(diào)整的捂敌,后面有介紹)。這樣只要一方得到了釋放了既琴,就可以成功避免死鎖占婉。當然這樣就會造成野指針。是的甫恩,比如Parent因為因為強指針計數(shù)器計數(shù)已經(jīng)到0了逆济,根據(jù)規(guī)則生命周期就結(jié)束了。但是此時Child還持有父類的弱引用磺箕,顯然如果Child此時用這個指針訪問Parent會引發(fā)致命的問題奖慌。為了別面這個問題,我們還規(guī)定: 弱指針必須先升級為強指針滞磺,才能訪問它所指向的目標對象升薯。

所以我們也可以說莱褒,若指針的主要使用就是解決循環(huán)引用的問題击困。下面具體看看它和強指針的區(qū)別。我們先從代碼上看

2广凸、wp的源碼解析:

代碼在RefBase.h 215行

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

    inline wp() : m_ptr(0) { }

    wp(T* other);   //構(gòu)造函數(shù)
    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;

    T*              m_ptr;
    weakref_type*   m_refs;
};

通過和sp相比阅茶,我們發(fā)現(xiàn)有如下區(qū)別:

  • 除了指向目標對象的m_ptr外,wp另外有一個m_refs指針谅海,類型為weakref_type脸哀。
  • 沒有重載 " -> " 、" * " 等運算符扭吁。
  • 有一個prmote方法將wp提升為sp撞蜂。
  • 目標對象的父類不是LightRefBase,而是RefBase

3侥袜、wp的構(gòu)造函數(shù):

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

通過和強指針的中的構(gòu)造函數(shù)進行對比蝌诡,我們發(fā)現(xiàn),wp并沒有直接增加目標對象的引用計數(shù)值枫吧,而是調(diào)用了createWeak()函數(shù)浦旱。這個函數(shù)是RefBase類的

3.1RefBase類

在代碼在RefBase.h 69行

class RefBase
{
public:
            void            incStrong(const void* id) const;   //增加強引用計數(shù)器的值
            void            decStrong(const void* id) const;  //減少強引用計數(shù)器的值

            void            forceIncStrong(const void* id) const;

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

    class weakref_type   //嵌套類,wp中用到的就是這個類
    {
    public:
        RefBase*            refBase() const;

        void                incWeak(const void* id);   //增加弱引用計數(shù)器的值
        void                decWeak(const void* id);  //減少弱引用計數(shù)器的值

        // 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();    //構(gòu)造函數(shù)
    virtual                 ~RefBase();   //析構(gòu)函數(shù)

    //! Flags for extendObjectLifetime()
    // 以下參數(shù)用于修改object的生命周期
    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嵌套了一個重要的類weakref_type九杂,也就是前面的m_refs指針所屬的類型颁湖。RefBase中還有一個mRefs的成員變量宣蠕,類型為weakref_impl。從名稱上來看甥捺,它應該是weak_type的實現(xiàn)類抢蚀。

3.2 weakref_impl類

在代碼在RefBase.cpp 64行

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;   //強引用計數(shù)器的值
    volatile int32_t    mWeak;   //弱引用計數(shù)器的值
    RefBase* const      mBase;      
    volatile int32_t    mFlags;

#if !DEBUG_REFS    //非Debug模式下,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     //debug的情況下

    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
        , mStrongRefs(NULL)
        , mWeakRefs(NULL)
        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
        , mRetain(false)
    {
    }

    ~weakref_impl()
    {
        bool dumpStack = false;
        if (!mRetain && mStrongRefs != NULL) {
            dumpStack = true;
            ALOGE("Strong references remain:");
            ref_entry* refs = mStrongRefs;
            while (refs) {
                char inc = refs->ref >= 0 ? '+' : '-';
                ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
                refs->stack.log(LOG_TAG);
#endif
                refs = refs->next;
            }
        }

        if (!mRetain && mWeakRefs != NULL) {
            dumpStack = true;
            ALOGE("Weak references remain!");
            ref_entry* refs = mWeakRefs;
            while (refs) {
                char inc = refs->ref >= 0 ? '+' : '-';
                ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
                refs->stack.log(LOG_TAG);
#endif
                refs = refs->next;
            }
        }
        if (dumpStack) {
            ALOGE("above errors at:");
            CallStack stack(LOG_TAG);
        }
    }

    void addStrongRef(const void* id) {
        //ALOGD_IF(mTrackEnabled,
        //        "addStrongRef: RefBase=%p, id=%p", mBase, id);
        addRef(&mStrongRefs, id, mStrong);
    }

    void removeStrongRef(const void* id) {
        //ALOGD_IF(mTrackEnabled,
        //        "removeStrongRef: RefBase=%p, id=%p", mBase, id);
        if (!mRetain) {
            removeRef(&mStrongRefs, id);
        } else {
            addRef(&mStrongRefs, id, -mStrong);
        }
    }

    void renameStrongRefId(const void* old_id, const void* new_id) {
        //ALOGD_IF(mTrackEnabled,
        //        "renameStrongRefId: RefBase=%p, oid=%p, nid=%p",
        //        mBase, old_id, new_id);
        renameRefsId(mStrongRefs, old_id, new_id);
    }

    void addWeakRef(const void* id) {
        addRef(&mWeakRefs, id, mWeak);
    }

    void removeWeakRef(const void* id) {
        if (!mRetain) {
            removeRef(&mWeakRefs, id);
        } else {
            addRef(&mWeakRefs, id, -mWeak);
        }
    }

    void renameWeakRefId(const void* old_id, const void* new_id) {
        renameRefsId(mWeakRefs, old_id, new_id);
    }

    void trackMe(bool track, bool retain)
    {
        mTrackEnabled = track;
        mRetain = retain;
    }

    void printRefs() const
    {
        String8 text;

        {
            Mutex::Autolock _l(mMutex);
            char buf[128];
            sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
            text.append(buf);
            printRefsLocked(&text, mStrongRefs);
            sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
            text.append(buf);
            printRefsLocked(&text, mWeakRefs);
        }

        {
            char name[100];
            snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
            int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
            if (rc >= 0) {
                write(rc, text.string(), text.length());
                close(rc);
                ALOGD("STACK TRACE for %p saved in %s", this, name);
            }
            else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this,
                      name, strerror(errno));
        }
    }

private:
    struct ref_entry
    {
        ref_entry* next;
        const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
        CallStack stack;
#endif
        int32_t ref;
    };

    void addRef(ref_entry** refs, const void* id, int32_t mRef)
    {
        if (mTrackEnabled) {
            AutoMutex _l(mMutex);

            ref_entry* ref = new ref_entry;
            // Reference count at the time of the snapshot, but before the
            // update.  Positive value means we increment, negative--we
            // decrement the reference count.
            ref->ref = mRef;
            ref->id = id;
#if DEBUG_REFS_CALLSTACK_ENABLED
            ref->stack.update(2);
#endif
            ref->next = *refs;
            *refs = ref;
        }
    }

    void removeRef(ref_entry** refs, const void* id)
    {
        if (mTrackEnabled) {
            AutoMutex _l(mMutex);

            ref_entry* const head = *refs;
            ref_entry* ref = head;
            while (ref != NULL) {
                if (ref->id == id) {
                    *refs = ref->next;
                    delete ref;
                    return;
                }
                refs = &ref->next;
                ref = *refs;
            }

            ALOGE("RefBase: removing id %p on RefBase %p"
                    "(weakref_type %p) that doesn't exist!",
                    id, mBase, this);

            ref = head;
            while (ref) {
                char inc = ref->ref >= 0 ? '+' : '-';
                ALOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref);
                ref = ref->next;
            }

            CallStack stack(LOG_TAG);
        }
    }

    void renameRefsId(ref_entry* r, const void* old_id, const void* new_id)
    {
        if (mTrackEnabled) {
            AutoMutex _l(mMutex);
            ref_entry* ref = r;
            while (ref != NULL) {
                if (ref->id == old_id) {
                    ref->id = new_id;
                }
                ref = ref->next;
            }
        }
    }

    void printRefsLocked(String8* out, const ref_entry* refs) const
    {
        char buf[128];
        while (refs) {
            char inc = refs->ref >= 0 ? '+' : '-';
            sprintf(buf, "\t%c ID %p (ref %d):\n",
                    inc, refs->id, refs->ref);
            out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
            out->append(refs->stack.toString("\t\t"));
#else
            out->append("\t\t(call stacks disabled)");
#endif
            refs = refs->next;
        }
    }

    mutable Mutex mMutex;
    ref_entry* mStrongRefs;
    ref_entry* mWeakRefs;

    bool mTrackEnabled;
    // Collect stack traces on addref and removeref, instead of deleting the stack references
    // on removeref that match the address ones.
    bool mRetain;

#endif
};
  • 從開頭的幾個變量大概可以猜出weakref_impl所做的工作镰禾,其中mStrong用于強引用計數(shù)思币,mWeak用于弱引用計數(shù)。宏DEBUG_REFS用于指示release或debug版本羡微,可以看出谷饿,在release版本下,addStrongRef,removeStrongRef相關(guān)的一系列方法都沒有具體實現(xiàn)妈倔,也就是說博投,這些方法實際上是用于調(diào)試的,我們在分析時完全可以用戶例會盯蝴。這樣一來毅哗,整體分析也清晰了很多。
  • Debug和Release版本都將mStrong初始化為INITIAL_STRONG_VALUE捧挺。這個值定義如下:

在代碼在RefBase.cpp 640行

#define INITIAL_STRONG_VALUE (1<<28)

而mWeak則初始化為0虑绵。上面的代碼并沒有引用計數(shù)器相關(guān)控制的實現(xiàn),真正有用的代碼在類聲明的外面闽烙。比如我們在wp構(gòu)造函數(shù)中遇到的createWeak函數(shù)翅睛,那讓我們來看一下RefBase::createWeak()函數(shù)

3.3 RefBase::createWeak()函數(shù)

在代碼在RefBase.cpp 572行

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);  //增加弱引用計數(shù)
    return mRefs;  //直接返回weakref_type對象
}

這個函數(shù)先增加了mRefs(也就是weak_impl類型成員變量)中的弱引用計數(shù)值,然后返回這個mRefs黑竞。

3.4 wp與RefBase

關(guān)于類的關(guān)系圖如下


  • 首先 wp中的m_ptr還是要指向目標對象(繼承自RefBase)捕发。RefBase提供了弱引用控制以及其他新的功能。
  • 其次 因為RefBase需要處理多種計數(shù)類型很魂,所以RefBase不直接使用int來保存應用計數(shù)器中的計數(shù)值扎酷,而是采用了weakref_type的計數(shù)器。另外wp也同時保存了這個計數(shù)器的地址遏匆,也就是wp中的m_refs和RefBase中的mRefs都指向了計數(shù)器法挨。其中wp是通過構(gòu)造函數(shù)中調(diào)用目標對象的createWeak來獲取計數(shù)器地址的,而計數(shù)器本身是由RefBase在構(gòu)造時創(chuàng)建的幅聘。
  • 整個wp機制看起來很復雜凡纳,但與強指針相比實際上只是啟動了一個新的計數(shù)器weakref_impl而已,其他所有工作都是圍繞如何操作這個計數(shù)器而展開的喊暖。雖然weakref_impl是RefBase的成員變量惫企,但是wp也可以直接控制它,所以整個邏輯顯得稍微有點混亂。

在createWeak中狞尔,mRefs通過incWeak增加了計數(shù)器的弱引用丛版。代碼如下:
在代碼在RefBase.cpp 391行

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 = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

這個函數(shù)真真的有用的語句就是android_atomic_inc(&impl->mWeak); ,它增加了mWeak計數(shù)器的值偏序,而其他都與調(diào)試有關(guān)页畦。

這樣當wp構(gòu)造完成以后,RefBase所持有的weakref_type計算器中的mWeak就為1研儒。后面如果有新的wp指向這個目標對象豫缨,mWeak還會持續(xù)增加。

上面是wp增加引用的邏輯端朵,那么如果sp指向它會怎么樣好芭?上面我們已經(jīng)說了sp會調(diào)用目標對象的incStrong方法來增加強引用計數(shù)器的值,當目標對象繼承自RefBase時冲呢,這個函數(shù)實現(xiàn)是

3.5 incStrong()函數(shù)

在代碼在RefBase.cpp 572行

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);  //增加弱引用計數(shù)值

    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);  //增加強引用計數(shù)器的值
    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;
    }
    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    ref

其實核心就兩行代碼

refs->incWeak(id);
const int32_t c = android_atomic_inc(&refs->mStrong); 

其實也就是同時增加弱引用和強引用的計數(shù)器的值。然后還要判斷目標對象是不是第一次被引用敬拓,其中C的變量得到的是"增加之前的值"邻薯,因而如果等于INITIAL_STRONG_VALUE就說明是第一次。這時候一方面回調(diào)onFirseRef通過對象自己被引用乘凸,另一方面要對mStrong值做下小調(diào)整厕诡。因為mStrong先是被置為INITIAL_STRONG_VALUE=1<<28,那么當一次增加時营勤,它就是1<<28+1灵嫌,所以還要再次減掉INITIAL_STRONG_VALUE才能得到1。

4冀偶、對象釋放

現(xiàn)在我們再來分析下目標對象在什么情況下會被釋放醒第。無非就是考察減少強弱引用時系統(tǒng)所遵循的規(guī)則,如下所示是decStrong的情況进鸠。
在代碼在RefBase.cpp 341行

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    //減少強引用計數(shù)器的值
    const int32_t c = android_atomic_dec(&refs->mStrong);
#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) {
        //減少強引用計數(shù)器的值已經(jīng)降為0
        //通知事件
        refs->mBase->onLastStrongRef(id);
        if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
             //刪除對象
            delete this;
        }
    }
    //減少弱引用計數(shù)器的值
    refs->decWeak(id);
}

整體流程如下:

  • 首先減少mStrong計數(shù)器。
  • 如果發(fā)現(xiàn)已經(jīng)減到0(即c==1)形病,就要回調(diào)onLastStrongRef通知這一事件客年,然后執(zhí)行刪除操作(如果標志是OBJECT_LIFETIME_STRONG)。
  • 最后減少弱引用計數(shù)器的值

PS:特別注意漠吻,減少強引用計數(shù)器的值還要同時減少弱引用計數(shù)器的值量瓜,即最后decWeak(id)。

4.1途乃、decWeak()函數(shù)

在代碼在RefBase.cpp 400行绍傲,實現(xiàn)代碼如下:

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);
    //減少弱引用的值
    const int32_t c = android_atomic_dec(&impl->mWeak);
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;

    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
        // This is the regular lifetime case. The object is destroyed
        // when the last strong reference goes away. Since weakref_impl
        // outlive the object, it is not destroyed in the dtor, and
        // we'll have to do it here.
        if (impl->mStrong == 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 {
        // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER}
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
            // is gone, we can destroy the object.
            delete impl->mBase;
        }
    }
}

通過閱讀上面的代碼,我們發(fā)現(xiàn)

  • 首先 顯示減少mWeak計數(shù)器的值
  • 其次 如果發(fā)現(xiàn)是0(即c==1),就直接返回
  • 如果發(fā)現(xiàn)不是0(即 c烫饼!=1)猎塞,則根據(jù)LIFETIME標志分別處理。

4.2杠纵、LIEFTIME的標志

LIEFTIME的標志是一個枚舉類荠耽,代碼如下
在代碼在RefBase.h 132行

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

每個目標對象都可以通過以下方法來更改它的引用規(guī)則

在代碼在RefBase.cpp 609行

void RefBase::extendObjectLifetime(int32_t mode)
{
    android_atomic_or(mode, &mRefs->mFlags);
}

所以實際上就是改變了mFlags標志值——默認情況下它是0,即OBJECT_LIFETIME_STRONG比藻。釋放規(guī)則則受強引用控制的情況铝量。有的人可能會想,既然是強引用控制银亲,那么弱引用還要干什么慢叨?理論上它確實可以直接返回了,不過還有些特殊情況务蝠。前面在incString函數(shù)里插爹,我們看到它同時增加了強、弱引用計數(shù)值请梢。而增加弱引用是不會同時增加強引用的赠尾,這說明弱引用的值一定會大于強引用值。當程序走到這里毅弧,弱引用數(shù)值一定為0气嫁,而強引用的的值有兩種可能:

  • 一種是強引用值為INITIAL_STRONG_VALUE,說明這個目標對象沒有被強引用過够坐,也就是說沒有辦法靠強引用指針來釋放目標寸宵,所以需要 delete impl->mBase
  • 另外一種就是在有強引用的情況下,此時要delete impl元咙,而目標對象會由強引用的decStrong來釋放梯影。

那么為什么在這里delete這個是計數(shù)器?weakref_impl既然是由RefBase創(chuàng)建的庶香,那么按道理來說應該由它來刪除甲棍。實際上RefBase也想做這個工作,只是力不從心赶掖。其析構(gòu)函數(shù)如下:

在代碼在RefBase.cpp 588行

RefBase::~RefBase()
{
    if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
        // we never acquired a strong (and/or weak) reference on this object.
        delete mRefs;
    } else {
        // life-time of this object is extended to WEAK or FOREVER, in
        // which case weakref_impl doesn't out-live the object and we
        // can free it now.
        if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
            // It's possible that the weak count is not 0 if the object
            // re-acquired a weak reference in its destructor
            if (mRefs->mWeak == 0) {
                delete mRefs;
            }
        }
    }
    // for debugging purposes, clear this.
    const_cast<weakref_impl*&>(mRefs) = NULL;
}

在這種情況下感猛,RefBase既然是有decStrong刪除的,那么從上面的decStrong的執(zhí)行順序來看mWeak值還不為0奢赂,因而并不會被執(zhí)行陪白。
如果弱引用控制下的判斷規(guī)則(即OBJECT_LIFTIME_WEAK),其實和decStrong中的處理一樣膳灶,要首先回調(diào)通知目標對象這一時間咱士,然后才能執(zhí)行刪除操作。

5、總結(jié)

關(guān)于Android的智能指針就分析到這里序厉,我們總結(jié)一下:

  • 1锐膜、智能指針分為強指針sp和弱指針wp
  • 2、通常情況下目標對象的父類是RefBase——這個基類提供了一個weakref_impl類型的引用計數(shù)器脂矫,可以同時進行強弱引用的控制(內(nèi)部由mStrong和mWeak提供計數(shù))
  • 3枣耀、當incStrong增加強引用,也會增加弱引用
  • 4庭再、當incWeak時只增加弱引用計數(shù)
  • 5捞奕、使用者可以通過extendObjectLifetime設(shè)置引用計數(shù)器的規(guī)則,不同規(guī)則下對刪除目標對象的時機判斷也是不一樣的
  • 6拄轻、使用者可以根據(jù)程序需求來選擇合適的智能指針類型和計數(shù)器規(guī)則

參考文獻

Android跨進程通信IPC之4——AndroidIPC基礎(chǔ)2

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颅围,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子恨搓,更是在濱河造成了極大的恐慌院促,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斧抱,死亡現(xiàn)場離奇詭異常拓,居然都是意外死亡,警方通過查閱死者的電腦和手機辉浦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門弄抬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宪郊,你說我怎么就攤上這事掂恕。” “怎么了弛槐?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵懊亡,是天一觀的道長。 經(jīng)常有香客問我乎串,道長店枣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任灌闺,我火速辦了婚禮艰争,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桂对。我一直安慰自己,他們只是感情好鸠匀,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布蕉斜。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宅此。 梳的紋絲不亂的頭發(fā)上机错,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音父腕,去河邊找鬼弱匪。 笑死,一個胖子當著我的面吹牛璧亮,可吹牛的內(nèi)容都是我干的萧诫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼枝嘶,長吁一口氣:“原來是場噩夢啊……” “哼帘饶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起群扶,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤及刻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竞阐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缴饭,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年骆莹,在試婚紗的時候發(fā)現(xiàn)自己被綠了颗搂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡汪疮,死狀恐怖峭火,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情智嚷,我是刑警寧澤卖丸,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布孵班,位于F島的核電站孝赫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏试和。R本人自食惡果不足惜猜嘱,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一衅枫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧朗伶,春花似錦弦撩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猾漫。三九已至,卻和暖如春感凤,著一層夾襖步出監(jiān)牢的瞬間悯周,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工陪竿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留禽翼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓族跛,卻偏偏與公主長得像闰挡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子庸蔼,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容