android系统核心机制 基础(01)智能指针wp & sp

112 篇文章 86 订阅

该系列文章总纲链接:android 系统核心机制基础 系列文章目录


本章关键点总结 & 说明:

以上是本模块的导图,整体概括了智能指针的几个要点,引用计数,弱转强,flag标志意义以及LightRefBase解读。

RefBase、sp和wp解读:
RefBase是Android中所有对象的始祖,类似于Java中的Object。在Android 中,RefBase结合sp和wp,实现了一套通过引用计数的方法来控制对象生命周期的机制。sp和wp的定义:sp是strong pointer,wp是weak pointer。sp和wp的目的,就是为了帮助健忘的程序员回收new出来的内存。 接下来这里以使用实例进行说明与分析。

1 影子对象

//类A从RefBase派生,RefBase是万物的始祖
class A:public RefBase
{
 //A没有任何自己的功能
}
int main()
{
  A* pA =new A;
  {
   //注意我们的sp,wp对象是在{}中创建的,下面的代码先创建sp,然后创建wp
   sp<A> spA(A);
   wp<A> wpA(spA);
   ...//正常其他的使用spA和wpA操作
    //大括号结束前,先析构wp,再析构sp
   }
}

@1 这里分析RefBase构造函数。代码如下:

RefBase::RefBase(): mRefs(new weakref_impl(this))
{
    //mRefs是RefBase的成员变量,类型是weakref_impl,我们暂且叫它影子对象,所以A有一个影子对象
}

mRefs是引用计数管理的关键类,需要进去观察。它是从RefBase的内部类weakref_type中派生出来的。声明如下:

//RefBase.h中weakref_type的声明
typedef typename RefBase::weakref_type weakref_type;
//RefBase.cpp中定义的派生关系
class RefBase::weakref_impl : public RefBase::weakref_type    //从RefBase的内部类weakref_type派生

C++的内部类和Java内部类相似,不同点是它需要一个显示的成员指向外部类对象。weakref_impl构造实现如下所示:

weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE) //强引用计数,初始值为0x1000000,即(1<<28)
        , mWeak(0)//弱引用计数,初始值为0
        , mBase(base))//该影子对象所指向的实际对象
        , mFlags(0)
    {
    }

new了一个A对象后,其实还new了一个RefBase::weakref_impl内部类对象,这里称它为影子对象,A为实际对象。影子对象成员中有两个引用计数:一个强引用,一个弱引用。在构造一个实际对象的同时还会悄悄地构造一个影子对象。
@2 程序继续运行,现在到了

sp<A> spA(A);

sp的构造函数,它的代码如下所示(sp是一个模板类,这里列举最关键sp摸板函数):

template<typename T> //根据程序特点,运行的是这个构造器
sp<T>::sp(T* other)  //这里的other就是刚才创建的pA
: m_ptr(other)       // sp保存了pA的指针
  {
    if (other) other->incStrong(this);//调用pA的incStrong
  }
...//其他构造器略过

 继续分析RefBase的incStrong中,它的代码如下所示:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;//mRefs就是刚才RefBase构造函数中new出来的内部影子对象
    refs->incWeak(id);
    refs->addStrongRef(id);//这句不考虑,Pass
    const int32_t c = android_atomic_inc(&refs->mStrong);//原子加1操作,并返回旧值。所以c=0x1000000,而mStrong变为0x1000001
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
    if (c != INITIAL_STRONG_VALUE)  { //如果c不是初始值,则表明这个对象已经被强引用过一次了,所以直接返回
        return;
    }
    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);//原子加操作,相当于执行refs->mStrong +(-0x1000000),最终mStrong=1
    refs->mBase->onFirstRef();    //如果是第一次引用,则调用onFirstRef,这个函数很重要,派生类可以重载这个函数,完成一些初始化工作。
}

这里继续分析refs->incWeak的方法,如下所示:

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);//这句不考虑,Pass
    const int32_t c __unused = android_atomic_inc(&impl->mWeak);//原子操作,影子对象的弱引用计数加1
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

sp构造初始化完成后RefBase中影子对象的强引用计数变为1,弱引用计数也变为1。
特殊说明:

  1. 影子对象的强弱引用计数的值,这是彻底理解sp和wp的关键。
  2. android_atomic_xxx是Android平台提供的原子操作函数,是多线程编程中的常见函数。 
  3. 因为这是release版走的分支。debug的代码是给创造RefBase、 sp,以及wp的人调试用的,因此分析流程时不考虑debug相关代码。以下几个函数不用考虑,仅为调试人员使用。

碰到它们,可以直接跳过,不影响流程的分析:

void addWeakRef(const void* /*id*/) { }
void addStrongRef(const void* /*id*/) { }
void removeStrongRef(const void* /*id*/) { }
void addWeakRef(const void* /*id*/) { }
void removeWeakRef(const void* /*id*/) { }
void printRefs() const { }
void trackMe(bool, bool) { }

@3 程序继续运行,现在到了

wp<A> wpA(spA)

wp有好几个构造函数,这里仅列出最关键的,如下所示:

template<typename T>

wp<T>::wp(T* other)
    : m_ptr(other)//wp的成员变量m_ptr指向实际对象
{
    if (other) m_refs = other->createWeak(this);//调用pA的createWeak,并且保存返回值到成员变量m_refs中
}
...//其他构造器略过

继续分析creatWeak,代码如下:

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);//影子对象的弱引用计数增加1
    return mRefs;      //返回影子对象
}

wp构造初始化完成后影子对象的弱引用计数将增加1。
@4 构造初始化结束总结:

  1. 现在弱引用计数为2,而强引用计数仍为1。
  2. wp中有两个成员变量,一个保存实际对象,另一个保存影子对象。
  3. sp只有一个成员变量用来保存实际对象,但这个实际对象内部已包含了对应的影子对象
  4. 正常使用该sp化和wp化是引用计数不变的,当销毁对象时,进入到析构方法时才会发生变化。

接下来开始进行析构的流程
@5 wp的析构,析构方法如下所示:

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

继续分析decWeak,代码如下:

void RefBase::weakref_type::decWeak(const void* id)
{
    //把基类指针转换成子类(影子对象)的类型,这种做法有些违背面向对象编程的思想
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);//这句不考虑,Pass
    const int32_t c = android_atomic_dec(&impl->mWeak);  //原子减1,返回旧值,c=2,而弱引用计数从2变为1
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;//c=2,直接返回

    //如果c为1,则弱引用计数为0,这说明没有弱引用指向实际对象,需要考虑是否释放内存
    // OBJECT_LIFETIME_XXX和生命周期有关系,1.3中会对此做详细解释
    //这里OBJECT_LIFETIME_WEAK=0,OBJECT_LIFETIME_STRONG=1
    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
        if (impl->mStrong == INITIAL_STRONG_VALUE) {
            delete impl->mBase;
        } else {
            delete impl;
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            delete impl->mBase;
        }
    }
}

这里因为之前sp和wp分别执行构造器,所以,弱引用计数为2。wp析构后,弱引用计数减1。但由于此时强引用计数和弱引用计数仍为1,所以没有对象被干掉,即没有释放实际对象和影子对象占据的内存。
@6 sp的析构,析构方法如下所示:

template<typename T>
sp<T>::~sp()
{
    if (m_ptr) m_ptr->decStrong(this);//调用实际对象的decStrong。由RefBase实现
}

继续分析decStrong,代码如下:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);//这句不考虑,Pass
    const int32_t c = android_atomic_dec(&refs->mStrong);

    //注意,此时强弱引用计数都是1,下面函数调用的结果是c=1,强引用计数为0
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);

    if (c == 1) {//对于我们的例子, c为1
        refs->mBase->onLastStrongRef(id);//调用onLastStrongRef,表明强引用计数减为0,对象有可能被delete
        //mFlags为0,所以会通过delete this把自己干掉。注意,此时弱引用计数仍为1
        // OBJECT_LIFETIME_XXX和生命周期有关系,1.3中会对此做详细解释
        if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }
   //注意,实际数据对象已经被干掉了,所以mRefs也没有用了,但是decStrong刚进来时就保存mRefs到refs了,所以这里的refs指向影子对象
    refs->decWeak(id);//调用影子对象decWeak
}

先看delete this的处理,它会导致A的析构函数被调用,A的析构函数,代码如下所示:

//A是继承RefBase的,因此,A的析构直接导致进入RefBase的析构。
RefBase::~RefBase()
{
    if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
        delete mRefs;
    } else {
        if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
            if (mRefs->mWeak == 0) { //这里弱引用计数不为0,而是1
                delete mRefs;
            }
        }
    }
    const_cast<weakref_impl*&>(mRefs) = NULL;
}

RefBase的delete this自杀行为没有把影子对象销毁,但还在decStrong中,可接着从delete this往下看,到影子对象的decWeak方法,代码如下:

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);

    //调用前影子对象的弱引用计数为1,强引用计数为0,调用结束后c=1,弱引用计数为0
    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;

    // OBJECT_LIFETIME_XXX和生命周期有关系,1.3中会对此做详细解释
    //这次弱引用计数终于变为0,并且mFlags为0, mStrong也为0。
    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
        if (impl->mStrong == INITIAL_STRONG_VALUE) {
            delete impl->mBase;
        } else {
            delete impl;//impl就是this,把影子对象自己干掉
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            delete impl->mBase;
        }
    }
}

@7 总结

  1. RefBase中有一个隐含的影子对象weakRefImpl,该影子对象内部有强弱引用计数。
  2. sp化后,强弱引用计数各增加1,sp析构后,强弱引用计数各减1。
  3. wp化后,弱引用计数增加1,wp析构后,弱引用计数减1。

完全彻底地消灭RefBase对象,包括让实际对象和影子对象灭亡,这些都是由强弱引用计数控制的,另外还要考虑flag的取值情况。当flag为0时,可得出如下结论:

  1. 强引用为0将导致实际对象被delete。
  2. 弱引用为0将导致影子对象被delete。

2 弱引用变成强引用

这里以使用实例进行说明与分析:

int main()
{
    A *pA =new A();
    wp<A> wpA(A);
    sp<A> spA = wpA.promote();//通过promote函数,得到一个sp
}

wp化后弱引用计数加1,所以此处wp化的结果是:影子对象的弱引用计数为1,强引用计数仍然是初始值0x1000000,wpA的promote函数是从一个弱对象产生一个强对象的重要函数,试看:

@1 由弱引用变成强引用的方法解析

template<typename T>

sp<T> wp<T>::promote() const
{
    sp<T> result;
    if (m_ptr && m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);//调用sp的构造函数
    }
    return result;
}

这里分析set_pointer,代码如下:

template<typename T>
void sp<T>::set_pointer(T* ptr) {
    m_ptr = ptr;
}

@2 由弱引用变成强引用的关键函数是attemptIncStrong,它的代码如下所示:

bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
    incWeak(id);//增加弱引用计数,此时弱引用计数变为2
    
    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);
    //该循环在多线程操作同一个对象时可能会循环多次。这里可以不去管它,目的就是使强引用计数增加1
    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
        if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
            break;
        }
        curCount = impl->mStrong;
    }
    // OBJECT_LIFETIME_XXX和生命周期有关系,1.3中会对此做详细解释
    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
        if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
            if (curCount <= 0) {
                decWeak(id);    //弱引用-1
                return false;
            }
            while (curCount > 0) {
                if (android_atomic_cmpxchg(curCount, curCount + 1,&impl->mStrong) == 0) {
                    break;
                }
                curCount = impl->mStrong;
            }

            if (curCount <= 0) {
                decWeak(id);    //弱引用-1并返回false,表示强引用计数增加失败
                return false;
            }
        } else {
            if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
                decWeak(id);    //弱引用-1并返回false,表示强引用计数增加失败
                return false;
            }
            curCount = android_atomic_inc(&impl->mStrong);
        }

        if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
            impl->mBase->onLastStrongRef(id);//交给派生类处理
        }
    }
    
    impl->addStrongRef(id);//这句不考虑,Pass

    curCount = impl->mStrong;
    while (curCount >= INITIAL_STRONG_VALUE) {
        ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE,"attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE",this);
        if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,&impl->mStrong) == 0) {
            break;
        }
        curCount = impl->mStrong;
    }
    return true;
}

这里对android_atomic_cmpxchg进行说明:android_atomic_cmpxchg(oldValue, newValue,&impl->mstrong);

逻辑解析:如果impl->mstrong == oldValue,则impl->mstrong == newValue,返回1;否则返回0;        
总结:promote完成后,相当于增加了一个强引用。由弱生强成功后,强弱引用计数均增加1。当前影子对象的强引用计数为1,弱引用计数为2。

同时这里对RefBase的extendObjectLifetime方法解读(解释OBJECT_LIFETIME_WEAK和OBJECT_LIFETIME_STRONG),RefBase中定义了一个关键枚举和一个方法extendObjectLifetime:

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

观察flags为OBJECT_LIFETIME_WEAK的情况,见实例代码:

class A:public RefBase
{
   publicA()
   {
      extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在构造函数中调用
   }
}

int main()
{
   A *pA =new A();
   wp<A> wpA(A);//弱引用计数加1
   {
      sp<A>spA(pA) //sp后,结果是强引用计数为1,弱引用计数为2
   }
....
}

@@2.1 sp的析构将直接调用RefBase的decStrong,它的代码如下所示:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);//这句不考虑,Pass
    const int32_t c = android_atomic_dec(&refs->mStrong);

    //注意,此时强引用计数都是1,弱引用计数是2,下面函数调用的结果是c=1,强引用计数为0
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {//对于我们的例子, c为1
        refs->mBase->onLastStrongRef(id);//调用onLastStrongRef,表明强引用计数减为0,对象有可能被delete
        //mFlags为0,所以会通过delete this把自己干掉。注意,此时弱引用计数仍为1
        //注意这句话。如果flags不是WEAK,将直接delete数据对象,现在我们的flags是WEAK,所以不会delete它
        if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }
    refs->decWeak(id);//调用影子对象decWeak,调用前引用计数为2
}

@@2.2 调用影子对象的decWeak。再来看它的处理,代码如下所示:

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);

    //调用前影子对象的弱引用计数为1,强引用计数为0,调用结束后c=1,弱引用计数为0
    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;    //c为2,弱引用计数为1,直接返回。


    //若到了wp析构之处,也会调用decWeak,调用上边的原子减操作后c=1,弱引用计数变为0,此时会继续往下运行。
    //由于mFlags为WEAK ,所以不满足if的条件
    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
        if (impl->mStrong == INITIAL_STRONG_VALUE) {
            delete impl->mBase;
        } else {
            delete impl;//impl就是this,把影子对象自己干掉
        }
    } else {//会走这里
        impl->mBase->onLastWeakRef(id);
        //由于flags值满足下面这个条件,所以实际对象会被delete,实际对象的delete会检查影子对象的弱引用计数,若它为0,则会把影子对象也delete掉。
        //由于影子对象的弱引用计数此时已经为0,所以影子对象也会被delete。
        if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            delete impl->mBase;
        }
    }
}

@3 总结
@@3.1 flags为LIFETIME_STRONG(即0),强引用计数控制实际对象的生命周期,弱引用计数控制影子对象的生命周期。强引用计数为0后,实际对象被delete。所以对于这种情况,应记住的是,使用wp时要由弱生强,以免收到segment fault信号。
@@3.2 flags为LIFETIME_WEAK(即1)强引用计数为0,弱引用计数不为0时,实际对象不会被delete。当弱引用计数减为0时,实际对象和影子对象会同时被delete。

3 轻量级的引用计数控制LightRefBase
RefBase是一个重量级的引用计数控制类。Android为我们提供了一个轻量级的RefBase,即LightRefBase。
@1 LightRefBase的实现,查看其代码:

template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(__attribute__((unused)) const void* id) const {
        //LightRefBase只有一个引用计数控制量mCount。incStrong的时候使它增加1
        android_atomic_inc(&mCount);
    }
    inline void decStrong(__attribute__((unused)) const void* id) const {
        //decStrong的时候减1,当引用计数变为零的时候,delete掉自己
        if (android_atomic_dec(&mCount) == 1) {
            delete static_cast<const T*>(this);
        }
    }
    //! DEBUGGING ONLY: Get current strong ref count.
    inline int32_t getStrongCount() const {
        return mCount;
    }

    typedef LightRefBase<T> basetype;

protected:
    inline ~LightRefBase() { }

private:
    friend class ReferenceMover;
    inline static void renameRefs(size_t n, const ReferenceRenamer& renamer) { }
    inline static void renameRefId(T* ref,
            const void* old_id, const void* new_id) { }

private:
    mutable volatile int32_t mCount;//引用计数控制变量
};

@2 使用实例(LightRefBase是一个模板类,其中类A是从LightRefBase派生,写法如下:

class A:public LightRefBase<A> //注意派生的时候要指明是LightRefBase<A>
{
public:
A(){};
~A(){};
};

从LightRefBase的定义中可以知道,它支持sp控制,因为它只有incStrong和decStrong。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值