码迷,mamicode.com
首页 > 移动开发 > 详细

Android 智能指针

时间:2014-06-15 18:37:20      阅读:435      评论:0      收藏:0      [点我收藏+]

标签:android   智能指针   内存泄漏   refbase引用计数   sp_wp   

   众多周知,指针在C/C++是一个很重要的手段,但是也是最让人麻烦的东西,容易出现空指针,或者内存泄漏,无非是new了一个对象,没有对应的delete,长时间运行导致系统崩溃!

在android系统中其实也有这个指针这个概念,只不过被用另外的一种安全机制弥补以上的麻烦。


一.概念:

android的中间层frameworks中的JNI以及往下的libraries 运行库层大部分使用的是C++来写的,目的是为了提高效率,在这些地方就需要用到指针了!

android 提供了一套机制为智能指针:

智能指针本身为一个对象,是一个模板类,可以当作一个控制类。有 sp  wp  这两个类,后面介绍。

通过对指针指向的对象进行引用计数控制,来决定delete对象,不会遗漏new出来无用的对象,只要还有指针还指向目标对象,那么就不会delete该对象 也杜绝了指针指空的现象!


智能指针用法分三种类型:

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 一个变量来计数,初始为0,有incStrong decStrong 这个两个方法 来操作 mCount 。

这个计数类比较简单,接口方法只有 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;
};


这个类有点复杂,知道几个重要的函数就可以了,这个计数类也提供了 incStrongdecStrong这两个控制计数的函数

所谓强指针,肯定是比轻量级的复杂,包含了轻量级的简单计数以外,还区分了计数的类型,分强引用的计数和弱引用的计数,这个两个计数器的值都能起到决定是否delete的作用,

这样做的原因是因为,如果在两个对象之间。互相通过指针引用了对象,当两个对象都不需要用到的时候,就无法单独回收一个对象,定义一个规则 在某个对象的强引用计数为0的时候 不管弱引用多少 都delete 就能解决这一问题

在嵌套引用中就需要设置delete 到底依赖规则,依赖强计数 还是 弱计数 !

这两种计数的变量在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)
    {
    }
...

}



可以看到前面两个就是两个计数变量,mBase代表这个类对应的RefBase类,

而mFlags就是上面说的依赖规则了,怎么来判定处理前面的两个计数变量!构造函数可以看到 默认为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\n", 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\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;
        }
    }
}

一样的,做了判断,如果发现到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\n", this, id, curCount);
#endif

    if (curCount == INITIAL_STRONG_VALUE) {
        android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
        impl->mBase->onFirstRef();
    }
    
    return true;
}


但是提升为强指针是要看目前弱指针对象是否已经delete,还要看对象有没有设置一些属性导致不被允许提升为强指针

一种情况是 对象整在被其它强指针引用 强引用肯定大于0  可以提升为强指针 然后增加对象强引用计数


第二种情况:

如果没有被其它的强指针引用而且强引用计数等于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 开头就直接增加了弱引用计数!

如果满足条件,往下就增加强引用计数.



五.强弱指针使用范例:

借用一个写的比较形象的范例:


#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("-----------------------\n");
                printf("Strong Ref Count: %d.\n", (strong  == INITIAL_STRONG_VALUE ? 0 : strong));
                printf("Weak Ref Count: %d.\n", ref->getWeakCount());
                printf("-----------------------\n");
        }
};

class StrongClass : public WeightClass
{
public:
        StrongClass()
        {
                printf("Construct StrongClass Object.\n");
        }

        virtual ~StrongClass()
        {
                printf("Destory StrongClass Object.\n");
        }
};


class WeakClass : public WeightClass
{
public:
        WeakClass()
        {
                extendObjectLifetime(OBJECT_LIFETIME_WEAK);

                printf("Construct WeakClass Object.\n");
        }

        virtual ~WeakClass()
        {
                printf("Destory WeakClass Object.\n");
        }
};




void TestStrongClass(StrongClass* pStrongClass)
{
        wp<StrongClass> wpOut = pStrongClass;
        pStrongClass->printRefCount();

        {
                sp<StrongClass> spInner = pStrongClass;
                pStrongClass->printRefCount();
        }

        sp<StrongClass> spOut = wpOut.promote();
        printf("spOut: %p.\n", 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.\n", spOut.get());
}



int main(int argc, char** argv)
{
        printf("Test Strong Class: \n");
        StrongClass* pStrongClass = new StrongClass();
        TestStrongClass(pStrongClass);

        printf("\nTest Weak Class: \n");
        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

退出大括号,销毁强指针,因为默认的强指针是依赖强引用的,上面有说 mFlag 设的是0 ,所在这个时候发现对象的强引用从1变为0了,就delete对象

最后再来用之前的弱指针提升到强指针,因为对象不存在了,所以失败 返回 0x0.


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

这个时候提升强指针也满足上面说到过的条件,所以返回0xa528

在程序退出时,弱指针也销毁,这个时候,弱引用计数为0 ,delete 对象!


到这里学习分析完毕!

撰写不易,转载请注明出处:http://blog.csdn.net/jscese/article/details/30070775









Android 智能指针,布布扣,bubuko.com

Android 智能指针

标签:android   智能指针   内存泄漏   refbase引用计数   sp_wp   

原文地址:http://blog.csdn.net/jscese/article/details/30070775

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!