Android framwork 分析之智能指针LightRefBase ,sp,wp,RefBase

来源:互联网 发布:软件加锁怎么设置 编辑:程序博客网 时间:2024/06/02 18:31

  Android的framework层都是由c++来实现的 大家都知道c++最令人头痛的莫过于内存泄漏了 ,如果是一个人开发还好 当new出来一个对象后 应该会记得delete掉 但是当多人开发的时候难免会出现这里调用了 那里忘记释放了 特别是对于Android是一个系统来说 那就更加了! 所以才有了用智能指针来处理这个问题,当没有地方引用的时候实现自动释放 避免内存泄漏或者野指针的问题。

 在分析源码的过程中时常会出现sp<T>之类的出现 那现在就来看看它的具体实现。

首先我们来看看:

LightRefBase

这个类是轻量级指针,它里面的代码很简单 就是有两个操作的方法以及一个用来记录引用的计数器变量:
template <class T>class LightRefBase{public:    inline LightRefBase() : mCount(0) { }//初始化引用的次数为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);        }    }    //获取该对象当前被引用的次数    inline int32_t getStrongCount() const {        return mCount;    }    typedef LightRefBase<T> basetype;protected:    inline ~LightRefBase() { }//析构private:    mutable volatile int32_t mCount;//用来记录对象的引用次数};

LightRefBase 它提供了引用计数的次数,删除以及增加的操作 它是一个计数器的基类,这个类中最重要的是 mutable volatile int32_t mCount; 这个类的任务就是维护mCount这个引用计数器, mutable实现互斥访问,volatile保证值的最新,具体的管理则是由sp类来管理 每当有一个对象被引用则调用sp的构造方法来增加一次引用 由于sp属于栈变量,所以根据C++的规则当超出范围的时候就会执行析构函数来减少一次引用,当引用次数为0的时候就会delete此对象,现在来看看sp类,它是一个模板类:

二、sp(Strong Pointer)


template <typename T>class sp{public:    inline sp() : m_ptr(0) { }//默认构造函数 初始化m_ptr为0    sp(T* other);//初始化T 这里的T代表是实际引用的对象     sp(const sp<T>& other);//拷贝构造函数 同理                                              template<typename U> sp(U* other);//带有返回值的构造函数    template<typename U> sp(const sp<U>& other);                                            ~sp();                                              // 操作符重载    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);                                              //动态来设置T的对象     void force_set(T* other);                                              // Reset    void clear();                                              // 操作符重载 这样的话当使用这些操作符 返回的就是引用的实际对象    inline  T&      operator* () const  { return *m_ptr; }    inline  T*      operator-> () const { return m_ptr;  }    inline  T*      get() const         { return m_ptr; }                                              // 宏定义 一些运算符    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;//这里的T就是实际引用的对象了};
再来看看他的构造函数和拷贝构造
template<typename T>sp<T>::sp(T* other)//这里的other实际就是LigthRefBase    : m_ptr(other)//把other赋值给m_ptr{    if (other) other->incStrong(this);//调用LigthRefBase的incstrong}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);//实际是调用LightRefBase的decStrong函数-1了 如果引用次数为0则delete该对象}

通过上面的分析应该就知道 如果一个对象要支持轻量级指针 那肯定得继承LightRefBase类 ,当对象类继承了LightRefBase 则代表其也有引用的计数器了 既然这样 那我们也来实现一个看看效果:
我们先定义一个MyLightClass然后继承自LightRefBase ,如果用NDK来写的话 比价麻烦 还要引入其源码里面的libutils.so 以及Include文件,所以我们直接在源码中写代码,这样引入头文件方便许多
#include <stdio.h>#include <utils/RefBase.h>using namespace android;class MyLightClass : public LightRefBase<MyLightClass>{public:        MyLightClass()        {                printf("开始调用构造函数\n");        }        virtual ~MyLightClass()        {                printf("析构\n");        }};int main(int argc, char** argv){        MyLightClass* light = new MyLightClass();        sp<LightClass> lp = light;        printf("当前的引用次数为%d\n", light->getStrongCount());        {                sp<LightClass> lp2 = lp;                <span style="font-family: Arial, Helvetica, sans-serif;">printf("当前的引用次数为%d\n", light->getStrongCount());</span>        }        <span style="font-family: Arial, Helvetica, sans-serif;">printf("当前的引用次数为%d\n", light->getStrongCount());</span>        return 0;}
现在来分析下我们写的代码 当执行到 sp<LightClass> lp=light 时会自动调用sp的构造函数初始化里面的m_ptr为LinghtClass的实际对象 进而调用LightRefBase里面的incStrong方法使引用计数m_count加一操作,所以此时打印的应该是1,然后在进入内嵌代码块 执行sp<LightClass> lp2=lp; 同样会执行加1操作 所以在打印的时候应该是2了,当执行完代码块里面的内容后 由于是局部变量保存在栈中 当执行完了以后所以就会自动掉用sp的析构函数 那么此时应该会调用LightRefBase的decStrong函数-1操作了 并且判断下当前是引用计数是不是为0 由于不为0 表明还有对象在用所以不会删除此对象 ,出了代码块再打印那么应该是1了,当我们执行完这个程序退出时候那么lp的局部变量会再次调用sp的析构函数 在把引用计数-1 然后判断当前是否为0 此时满足条件 所以就会delete此对象 也就是我们new的 light对象了;我们在external目录新建此文件为 MyLightClass.cpp

由于写的代码是在源码中 为了参与编译所以我们得写一个Android.mk文件在external目录下
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_MODULE := mylightLOCAL_SRC_FILES := MyLightClass.cppLOCAL_SHARED_LIBRARIES := \        libcutils \        libutilsinclude $(BUILD_EXECUTABLE)

上面的mk文件代码我们要编译成可执行文件 也就是在system里面bin目录
然后执行编译 这里我们使用mmm命令只编译单模块
xielinhua@ubuntu:~/Downloads/Android$ mmm ./external/mylight

编译完成后我们就可以来打包:
xielinhua@ubuntu:~/Downloads/Android$make snod

然后在打开模拟器来执行刚才编译好的有我们写的代码
xielinhua@ubuntu:~/Downloads/Android$  emulator -kernel ./kernel/goldfish/arch/arm/boot/zImage &
最后来执行我们刚才写的程序看看他的打印是不是我们刚才分析的一样呢:
xielinhua@ubuntu:~/Downloads/Android$ adb shell
root@android:/ # cd system/bin/ root@android:/system/bin # ./mylight    开始调用构造函数当前次数为1当前次数为2当前次数为1析构


恩 现在应该明白轻量指针是怎么来控制对象的了 不过当我们遇到复杂一点的比如说A引用了B  B也引用了A这种情况的话 两者相互引用的话那么利用上面的显然是释放不了的,因为程序再执行的过程中会判断 对象的计数是否为0才释放,假设程序只执行一边的代码(比如就执行A类里面的代码,A里面的B引用其他类里面的对象,但是没有去执行那边的类代码)时候那么这两个对象都得不到释放  因为对象减一操作好还不为0!
Android中它采用了对象的引用计数同时存在强引用和弱引用两种计数。例如A引用B则B的强引用计数和弱引用计数+1,而B引用A则A仅仅弱引用数+1,在回收时只要对象的强引用计数为0,则不管弱引用数是否为0都进行回收,类似于死锁解决中的强制释放资源,那现在我们来看看更强大的引用计数器 RefBase 

三、RefBase

class RefBase{public:void            incStrong(const void* id) const;//增加一个引用计数void            decStrong(const void* id) const;//减少一个引用计数void            forceIncStrong(const void* id) const;class weakref_type//内部类 主要是通过这个类来实现强弱引用的计数{public:RefBase*            refBase() const;//实际对象void                incWeak(const void* id);void                decWeak(const void* id);//减少弱引用的次数bool                attemptIncStrong(const void* id);//! This is only safe if you have set OBJECT_LIFETIME_FOREVER.bool                attemptIncWeak(const void* id);};weakref_type*   createWeak(const void* id) const;//创建一个管理引用的对象weakref_type*   getWeakRefs() const;protected:RefBase();virtual                 ~RefBase();//! Flags for extendObjectLifetime()对象的生命周期控制 默认为0则代表是受强引用的控制 enum {OBJECT_LIFETIME_WEAK    = 0x0001,//代表受弱引用的控制OBJECT_LIFETIME_FOREVER = 0x0003//代表不受控制 自己来管理};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);weakref_impl* const mRefs;//看名字就知道weakref_type的实现类};

由此可见 在RefBase里面跟ligthRefBase不一样 它使用的是一个类 weakref_impl来管理对象的强弱引用,
首先来看看RefBase的构造函数:
RefBase::RefBase()    :mRefs(new weakref_impl(this))//构造的时候 new了一个weakref_impl对象{  //mRefs是RefBase的成员变量,类型是weakref_impl}

那先来看看weakref_impl类的初始化:
weakref_impl(RefBase* base)        :mStrong(INITIAL_STRONG_VALUE) //强引用计数,初始值为0x1000000        ,mWeak(0) //弱引用计数,初始值为0        ,mBase(base)//所指向的实际对象        ,mFlags(0)//生命周期受什么影响 默认是0 也就是强引用        ,mStrongRefs(NULL)        ,mWeakRefs(NULL)        ,mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)        ,mRetain(false)    {     }
我们再来举一个列子:
A a=new A();sp<A> sa=a;
这里是new了一个A对象a  然后赋值给了 sp<A> sa;  那现在来看看sp的构造函数:
template<typename T>sp<T>::sp(T* other)        : m_ptr(other) {    if (other)        other->incStrong(this);//这里调用的是实际对象的incStrong方法}

这里的A如果要实现智能指针 那一定是继承了RefBase类了,那other->incStrong(this)实际调用的是RefBase的对应方法:

void RefBase::incStrong(const void* id) const{    weakref_impl* const refs = mRefs;    refs->incWeak(id);//增加弱引用的计数        refs->addStrongRef(id);//debug 正式不做处理    const int32_t c = android_atomic_inc(&refs->mStrong);//原子加1 后返回+1前的值    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);    if (c != INITIAL_STRONG_VALUE)  {//判断INITIAL_STRONG_VALUE是否等于c 如果不等于那就代表已经引用了 所以返回        return;    }    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);    refs->mBase->onFirstRef();//第一次被引用}
上面的方法首先调用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 = android_atomic_inc(&impl->mWeak);//原子操作加1    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);}
可以看到当调用incStrong方法的时候会使强引用加1操作 同时弱引用也增加1 当sp退出作用域的时候会调用 析构函数:
template<typename T>sp<T>::~sp() {    if (m_ptr)//如果m_ptr不为空则满足条件 m_ptr是实际对象这里满足        m_ptr->decStrong(this);}
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    LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);#endif    LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);    if (c == 1) {//如果对象的引用计数为0 则满足条件        const_cast<RefBase*>(this)->onLastStrongRef(id);        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {//如果对象生命周期是受弱引用的话还不能删除该对象 否则就delete 自己了            delete this;        }    }    refs->removeWeakRef(id);    refs->decWeak(id);}

上面可以看到删除一个强引用并且判断是不是要删除对象自己 然后再减少弱引用 再来看看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);//弱引用-1操作    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) {//如果是受强引用的控制 则进入        if (impl->mStrong == INITIAL_STRONG_VALUE) {//判断是不是初始值 如果是(代表弱引用计数不是通过强引用的方式来加1)那么删掉实际对象 否则删掉管理计数的对象 也就是 weakref_impl实际对象则在Refbase中的decStrong中删除            delete impl->mBase;         } else {            delete impl;        }    } else {        impl->mBase->onLastWeakRef(id);        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {            delete impl->mBase;//如果是受弱引用控制 那么就删掉实际对象         }    }}
decWeak函数首先是把弱引用-1操作 如果-1前的c是不等于那么久返回 代表还有对象再引用 。如果没有对象引用的话那么继续往下执行 首先判断它的生命周期是不是受强引用的控制 如果是的话,那么它有两种情况 ,一种是这个对象是强引用初始化的 它会分别把强引用和弱引用计数加一;一种是没有初始化强引用 直接是弱引用来初始化的 这样只会增加弱引用的计数加一; 既然这样的话 就代表强引用可能没有初始化 所以会判断下 如果是没有初始化强引用 则直接删除对象 因为若引用的计数是大于等于强引用的!而这里的弱引用的计数为0 那肯定代表强引用是0了!如果对象曾经是被强引用过的 那么直接删除impl也就是计数的管理类 因为再初始化sp的时候我们new了这个对象 !因为上面再删除强引用计数的时候已经把实际对象给删除了 到此所有的对象也就都释放了!这是在对象生命周期受强引用控制的时候 那如果不是的话 调用onLastWeakRef首先通知这个对象可能马上要释放了 再判断该对象是不是受弱引用控制的 如果是那么此时弱引用为0了 也就是可以删除实际对象了  那么此时会调用RefBase的析构

再来看看RefBase的析构函数:
<pre name="code" class="cpp">RefBase::~RefBase(){//    LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs);    if (mRefs->mWeak == 0) {//如果弱引用计数为0 那么删除引用计数管理 也就是weakref_impl的对象//        LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this);        delete mRefs;    }}


跟着上面分析继续!在decWeak方法执行delete impl->mBase时由于这里满足条件 所以会删除计数管理  现在知道为什么在decWeak函数中 如果如果对象生命周期受强引用控制但是又不是使用强引用初始化的话   为什么会delete impl了  因为impl是RefBase  new出来的通常应该是谁new谁delete的 主要是因为RefBase的析构函数会判断弱引用是不是为0 但是这里的判断是不为0 的 所以才会在这里delete impl的! 
  通过上面可知 当对象a 超过了作用域的话那么 就会调用sp的析构函数  然后就会调用RefBase的decStrong方法 减少强弱引用计数的值 根据相应的生命周期来释放a的对象!!
下面来看看wp 也就是弱指针

四、wp

我们先来看看头文件的定义
template <typename T>class wp{public:    typedef typename RefBase::weakref_type weakref_type;        inline wp() : m_ptr(0) { }//初始化m_ptr    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(==)    COMPARE(!=)    COMPARE(>)    COMPARE(<)    COMPARE(<=)    COMPARE(>=)private:    template<typename Y> friend class sp;    template<typename Y> friend class wp;    T*              m_ptr;//实际对象    weakref_type*   m_refs;//操作计数类管理类
从上面可以看到有两个成员变量 T 实际对象 也就是RefBase 类 里面有个管理类 weakref_impl* const mRefs ;weakref_type*  m_refs这是用来干嘛的呢?

先来看看wp的构造函数:
template<typename T>wp<T>::wp(T* other)    : m_ptr(other)//初始化m_ptr{    if (other) m_refs = other->createWeak(this);//调用对象的createWeak方法 也就是RefBase的方法}
再来看看createWeak方法:
RefBase::weakref_type* RefBase::createWeak(const void* id) const{    mRefs->incWeak(id);//调用incWeak 增加对象的弱引用计数    return mRefs;//返回操作计数的管理对象 也就是weakref_type的实现类}

可以看到wp的构造函数不像sp构造一样会同时增加强弱引用 wp只增加了弱引用 ;调用完createWeak后 m_refs也就指向了mRefs了也就是实际的操作管理计数
再看看析构函数:
template<typename T>wp<T>::~wp(){    if (m_ptr) m_refs->decWeak(this);//减一操作}
弱指针是不能操作对象的因为他没有重载* ->操作费 但是他有一个方法升级成强指针来操作对象
template<typename T>sp<T> wp<T>::promote() const{    return sp<T>(m_ptr, m_refs);}
sp的构造函数:

template<typename T>sp<T>::sp(T* p, weakref_type* refs)    : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0){}
这里首先要判断p是否为空 也就是实际对象还在不在,还要判断attemptIncStrong是否是true  是的话才能升级为强指针
那我们来看看这个函数:
bool RefBase::weakref_type::attemptIncStrong(const void* id){incWeak(id);//增加弱引用 weakref_impl* const impl = static_cast<weakref_impl*>(this);int32_t curCount = impl->mStrong;//强引用的个数//如果强引用的个数大于0并且还不等于初始值 那对象有强引用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) {//没有强引用过 还是初始值//判断生命周期是不是受弱引用控制 onIncStrongAttempted()默认返回true allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK/*不受弱引用控制的话为true 代表允许升级*/|| impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);} else {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);}}//这里代表成功升级 那么强弱都加1impl->addWeakRef(id);impl->addStrongRef(id);#if PRINT_REFSLOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);#endifif (curCount == INITIAL_STRONG_VALUE) {android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);impl->mBase->onFirstRef();}return true;}

当弱指针升级成功 那么就可以想强指针一样来操作对象了 !



 


0 0
原创粉丝点击