Android-スマートポインタ


多くの周知のように、ポインタはC/C++で1つのとても重要な手段で、しかし最も面倒なもので、空のポインタが現れやすくて、あるいはメモリの漏れが現れて、newが1つの対象にほかならなくて、対応するdeleteがなくて、長い間運行してシステムの崩壊を招きます!
Androidシステムにもこのポインタという概念がありますが、上記のトラブルを別のセキュリティメカニズムで補うにすぎません.
一.コンセプト:
Androidの中間層frameworksのJNIおよび下のlibraries実行ライブラリ層の大部分はC++で書かれていますが、効率を高めるためにはポインタが必要です!
Androidはスマートポインタのためのメカニズムを提供しています.
インテリジェントポインタ自体はオブジェクトであり、テンプレートクラスであり、制御クラスとして使用できます.sp wpという2つのクラスがありますが、後で紹介します.
ポインタが指すオブジェクトを参照カウント制御することでdeleteオブジェクトを決定し、newが出てくる無駄なオブジェクトを漏らさず、ポインタがターゲットオブジェクトを指す限り、deleteそのオブジェクトもポインタが空を指す現象を根絶することはありません!
スマートポインタの使用方法は3種類あります.
1.軽量ポインタ
2.強針
3.弱ポインタ
二.軽量ポインタ:
ターゲットオブジェクトの参照カウントクラスは、/frameworks/native/include/utils/RefBase.hのLightRefBaseクラス、つまりスマートポインタが指すオブジェクトはLightRefBaseという親または間接親に継承されます!
template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(const void* id) const {
        android_atomic_inc(&mCount);
    }
    inline void decStrong(const void* id) const {
        if (android_atomic_dec(&mCount) == 1) {
            delete static_cast<const T*>(this);
        }
    }

...

}

mCountは、incStrong decStrongという2つの方法でmCountを操作する初期0の変数でカウントされることがわかります.
このカウントクラスは比較的簡単で、インタフェースの方法はspクラスしかないので、私の理解の軽量級のポインタはカウントクラスが比較的簡単なため、スマートポインタ制御クラスと強いポインタのタイプは同じようにspです!
spクラスを見てみましょう.
android4.2/frameworks/native/include/utils/strongPointer.hで定義する.
class sp
{
public:
    inline sp() : m_ptr(0) { }

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

    ~sp();

...

}

spはスマートポインタオブジェクトであり,あるオブジェクトを指すと作成され,あるオブジェクトを指す必要がない場合,すなわちこのポインタが破棄されると,それぞれ構造関数と構造関数が呼び出される.
spポインタクラスの構造と構造を見てみましょう.
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
  {
    if (other) other->incStrong(this);
  }

template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr)
  {
    if (m_ptr) m_ptr->incStrong(this);
  }
template<typename T>
sp<T>::~sp()
{
    if (m_ptr) m_ptr->decStrong(this);
}

参照されたオブジェクトが別々に呼び出されていることがわかります.
incStrongと
decStrongそれぞれ対応
LightRefBaseのカウントをプラス1とマイナス1にすると、mCount=1のときにdecStrongがdeleteオブジェクトになることがわかります!
三.強いポインタ:
軽量レベルのポインタとは異なり、強いポインタはRefBaseクラスでカウントされ、同じように強いポインタを必要とするものもこのクラスを継承しなければならず、/frameworks/native/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 ReferenceMover;
    static void moveReferences(void* d, void const* s, size_t n,
            const ReferenceConverterBase& caster);

private:
    friend class weakref_type;
    class weakref_impl;
    
                            RefBase(const RefBase& o);
            RefBase&        operator=(const RefBase& o);

        weakref_impl* const mRefs;
};


このクラスは少し複雑で、いくつかの重要な関数を知っていればいいです.このカウントクラスはincStrongとdecStrongの2つの制御カウントの関数も提供しています.
強ポインタとは、軽量級よりも複雑に違いないが、軽量級の単純カウントを含むほか、カウントのタイプ、強引用のカウントと弱引用のカウントを区別し、この2つのカウンタの値はdeleteの有無を決める役割を果たすことができる.
なぜなら、2つのオブジェクトの間にある場合です.互いにポインタでオブジェクトを参照し、両方のオブジェクトを使用する必要がない場合、オブジェクトを単独で回収することはできません.あるオブジェクトの強い参照カウントが0の場合、弱い参照の数にかかわらずdeleteを定義すると、この問題を解決できます.
ネストされたリファレンスではdeleteがルールに依存するか、強いカウントに依存するか、弱いカウントに依存するかを設定する必要があります.
この2つのカウントの変数はRefBaseクラスでweakref_implクラス提供、すなわちmRefs、weakrefを見てください.implクラス:
/frameworks/base/libs/utils/RefBase.cpp:
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;
    volatile int32_t    mWeak;
    RefBase* const      mBase;
    volatile int32_t    mFlags;
#if !DEBUG_REFS

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

}

前の2つが2つのカウント変数であり、mBaseはこのクラスに対応するRefBaseクラスを表しています.
mFlagsは上で述べた依存規則で、どのように前の2つのカウント変数を処理するかを判定します!コンストラクション関数のデフォルトは0です.RefBaseクラスのweakref_に対応する列挙typeクラス:
    enum {
        OBJECT_LIFETIME_STRONG  = 0x0000,
        OBJECT_LIFETIME_WEAK    = 0x0001,
        OBJECT_LIFETIME_MASK    = 0x0001
    };

軽量レベルと同様に、スマートポインタがオブジェクトを指す場合、ポインタを呼び出す構造であり、ここで強いポインタはspによって実際にオブジェクトに呼び出されるincStrongであり、
つまりRefBase::incStrong:
void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);

...

}

android4.2 incWeakに弱い参照カウントを追加し、addStrongRefに強いカウントを追加します.強弱カウントはすべて+1.
実際の処理関数はandroid_atomic_inc(&refs->mStrong)
同様に破棄時にRefBase::decStrongを呼び出す:
void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = android_atomic_dec(&refs->mStrong);
#if PRINT_REFS
    ALOGD("decStrong of %p from %p: cnt=%d
", this, id, c); #endif ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); if (c == 1) { refs->mBase->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { delete this; } } refs->decWeak(id); }
実際の処理関数は
android_atomic_dec(&refs->mStrong)
強カウントの操作が完了すると、0になったら、依存する処理ルールが強参照になると判断され、deleteというオブジェクトが表示されます!
そうでなければ、弱い参照カウント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 = 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
", 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; } } }

同じように、判断をして、0を見つけてルールが弱い引用処理であればdelete
四.弱いポインタ:
弱ポインタで用いられるカウントクラスは,強ポインタと同様にRefBaseというクラスである.制御するインテリジェントポインタが違うのはwpクラスで、/frameworks/native/include/utils/RefBaseに定義されています.h
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();

...

    // promotion to sp
    
    sp<T> promote() const;

...

}

呼び出しは上記と同様に、構造と構造関数を直接見ます.
template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) m_refs = other->createWeak(this);
}

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

RefBaseクラスの実装関数をそれぞれ呼び出します.
構築:
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

ここのmRefsはweakref_implクラスのオブジェクトは、上に述べたように、次のように調整されます.
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);
    const int32_t c = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

ここでは弱参照カウント+1
プロファイル:
直接呼び出し先:
RefBase::weakref_type::decWeak
同上!
また、弱いポインタには、ターゲットオブジェクトを直接操作できない特性があります.弱いポインタクラスには*と->操作記号が再ロードされていません.
弱いポインタはpromote()メソッドで強いポインタにアップグレードできます!
実装関数:RefBase::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;
    ALOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow",
               this);
    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
        if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
            break;
        }
        curCount = impl->mStrong;
    }
    
    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
        bool allow;
        if (curCount == INITIAL_STRONG_VALUE) {
            // Attempting to acquire first strong reference...  this is allowed
            // if the object does NOT have a longer lifetime (meaning the
            // implementation doesn't need to see this), or if the implementation
            // allows it to happen.
            allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
                  || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
        } else {
            // Attempting to revive the object...  this is allowed
            // if the object DOES have a longer lifetime (so we can safely
            // call the object with only a weak ref) and the implementation
            // allows it to happen.
            allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
                  && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
        }
        if (!allow) {
            decWeak(id);
            return false;
        }
        curCount = android_atomic_inc(&impl->mStrong);

        // If the strong reference count has already been incremented by
        // someone else, the implementor of onIncStrongAttempted() is holding
        // an unneeded reference.  So call onLastStrongRef() here to remove it.
        // (No, this is not pretty.)  Note that we MUST NOT do this if we
        // are in fact acquiring the first reference.
        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
", this, id, curCount); #endif if (curCount == INITIAL_STRONG_VALUE) { android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); impl->mBase->onFirstRef(); } return true; }

ただし、強いポインタに昇格するには、現在の弱いポインタオブジェクトがdeleteされているかどうかを確認し、オブジェクトにいくつかの属性が設定されているかどうかを確認して、強いポインタに昇格することを許可されません.
1つは、オブジェクト全体が他の強いポインタで参照される強い参照が0より大きい場合、強いポインタに昇格し、オブジェクトの強い参照カウントを増加させることができます.
2つ目のケース:
他の強いポインタで参照されておらず、強い参照数がINITIAL_に等しい場合STRONG_VALUEは次のようになります.
allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
                  || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);

そうでなければ、次のようになります.
allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
&& impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);

allowがfalseの場合、強いポインタに昇格できないことを表します.attempIncStrongの先頭に弱い参照カウントが直接追加されるため、ターゲットオブジェクトの弱い参照カウントを小さくします.
条件を満たすと、以下に強い参照カウントを増加する.
五.強弱ポインタの使用例:
1つの比較イメージの例を借ります.
#include <stdio.h>
#include <utils/RefBase.h>

#define INITIAL_STRONG_VALUE (1<<28)

using namespace android;

class WeightClass : public RefBase
{
public:
        void printRefCount()
        {
                int32_t strong = getStrongCount();
                weakref_type* ref = getWeakRefs();

                printf("-----------------------
"); printf("Strong Ref Count: %d.
", (strong == INITIAL_STRONG_VALUE ? 0 : strong)); printf("Weak Ref Count: %d.
", ref->getWeakCount()); printf("-----------------------
"); } }; class StrongClass : public WeightClass { public: StrongClass() { printf("Construct StrongClass Object.
"); } virtual ~StrongClass() { printf("Destory StrongClass Object.
"); } }; class WeakClass : public WeightClass { public: WeakClass() { extendObjectLifetime(OBJECT_LIFETIME_WEAK); printf("Construct WeakClass Object.
"); } virtual ~WeakClass() { printf("Destory WeakClass Object.
"); } }; void TestStrongClass(StrongClass* pStrongClass) { wp<StrongClass> wpOut = pStrongClass; pStrongClass->printRefCount(); { sp<StrongClass> spInner = pStrongClass; pStrongClass->printRefCount(); } sp<StrongClass> spOut = wpOut.promote(); printf("spOut: %p.
", spOut.get()); } void TestWeakClass(WeakClass* pWeakClass) { wp<WeakClass> wpOut = pWeakClass; pWeakClass->printRefCount(); { sp<WeakClass> spInner = pWeakClass; pWeakClass->printRefCount(); } pWeakClass->printRefCount(); sp<WeakClass> spOut = wpOut.promote(); printf("spOut: %p.
", spOut.get()); } int main(int argc, char** argv) { printf("Test Strong Class:
"); StrongClass* pStrongClass = new StrongClass(); TestStrongClass(pStrongClass); printf("
Test Weak Class:
"); WeakClass* pWeakClass = new WeakClass(); TestWeakClass(pWeakClass); return 0; }

走り出した結果:
Test Strong Class: 
Construct StrongClass Object.
-----------------------
Strong Ref Count: 0.
Weak Ref Count: 1.
-----------------------
-----------------------
Strong Ref Count: 1.
Weak Ref Count: 2.
-----------------------
Destory StrongClass Object.
spOut: 0x0.

まず弱いポインタでオブジェクトを指すため、オブジェクトの強い参照数は0、弱い参照数は1であることがわかります.
そして途中で1つの強いポインタでオブジェクトを指し、オブジェクトの2つの参照カウントはすべて1を加算します.
カッコを外して、強いポインタを破棄します.デフォルトの強いポインタは強い参照に依存しているので、mFlagは0を設定しています.このとき、オブジェクトの強い参照が1から0になったことがわかります.deleteオブジェクトです.
最後に前の弱いポインタで強いポインタに上げる、オブジェクトが存在しないため失敗して0 x 0に戻る.
Test Weak Class: 
Construct WeakClass Object.
-----------------------
Strong Ref Count: 0.
Weak Ref Count: 1.
-----------------------
-----------------------
Strong Ref Count: 1.
Weak Ref Count: 2.
-----------------------
-----------------------
Strong Ref Count: 0.
Weak Ref Count: 1.
-----------------------
spOut: 0xa528.
Destory WeakClass Object.

この弱いポインタのテストの前には、オブジェクトの作成時に参照カウントの依存規則を弱い参照カウントに依存するように設定するのとは異なります.
だから途中でカッコを外すとdeleteはしません
このとき、強いポインタを上げることも上記の条件を満たすので、0 xa 528に戻ります.
プログラムが終了すると、弱いポインタも破棄され、このとき、弱い参照カウントは0、deleteオブジェクト!
ここまで勉強して分析しました!
書くのは容易ではありません.転載するには出典を明記してください.http://blog.csdn.net/jscese/article/details/30070775