Android智能指针RefBase、sp、wp解析

[TOC]
在Android系统中,Native层的代码基本都是C++写的,C++跟Java不一样,C++没有垃圾回收机制,C++代码中难于管理new出来对象的释放,稍有不慎就造成内存泄漏。针对此问题,Android中提出了一套类似Java垃圾回收机制的智能指针,采用强指针sp(Strong Pointer)和弱指针wp(Weak Pointer)对目标对象进行应用,实现对象的自动回收。下面我们将从C++的基础知识入手,对Android的智能指针展开逐步的分析。

1 相关基础知识

Android的智能指针,巧妙的运用C++的基础特性,实现对象的自动释放,我们就先来看看都用了C++的什么特性。

1.1作用域

标记变量的有效范围。从作用域上来看,可以将对象分为全局对象、局部对象、静态全局对象和静态局部对象。

一般来说,局部变量的有效作用域从它的定义点开始,到和定义变量之前最邻近的开括号配对的第一个闭括号,也就是说,作用域由变量所在的最近一对{}括号确定。

演示代码1

void testScope() {
  Sheep bigSheep; //局部对象
  {
    Sheep smallSheep; // 局部对象
  }// smallSheep的作用域结束
}// bigSheep的作用域结束

1.2对象内存空间的分配、释放

从内存分配空间来看,可将对象分为栈对象和堆对象。栈对象在作用域结束后会自动释放,而堆对象需要手动显示的释放。

演示代码2

void testMemoryMap() {
  Sheep sheep; // 栈对象,testMemoryMap调用结束后自动释放
  Sheep* pSheep; // 堆对象,需要手动释放
  delete pSheep; // 释放pSheep指向的对象
  pSheep = 0; //将pSheep指向NULL,防止造成野指针
}

图1-1是内存空间的分配示意图。

图 1�内存空间的分配、释放

1.3原子操作函数

定义在system/core/libcutils/Atomic.c,依赖于具体的芯片平台。原子操作函数特点:线程安全,返回旧值。如下,Android原子操作函数一览表。

函数名 功能
int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) 加函数,返回旧值,*ptr = *ptr + increment
int32_t android_atomic_inc(volatile int32_t *addr) 自增操作,返回旧值,*ptr = *ptr + 1
int32_t android_atomic_dec(volatile int32_t *addr) 自减操作, 返回旧值,*ptr = *ptr - 1
int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) 位与操作,返回旧值,*ptr = *ptr & value
int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) 位或操作,返回旧值,*ptr = *ptr or value
int android_atomic_cmpxchg(int32_t old_value, int32_t new_value, volatile int32_t *ptr) 如果addr == oldvalue,就会执行addr = new_value的操作,然后返回0,否则返回1

1.4引用计数的原理

栈对象在生命周期,即作用域结束后自动释放,所以我们这里讨论的是堆对象的引用,也就是指针对象。

图1-2是指针引用时,利用引用数管理实际对象释放的原理图。

图�2 引用计数原理

引用计数的原理很简单,当引用某个对象时,使其引用数+1;引用结束时,使其引用数-1;当引用数为0时,delete掉实际对象。
根据前面的原理,引出两个问题,带着这两个问题,我们来看看Android是怎么实现的。

  • 怎么管理引用数?
  • 怎么判断引用开始和结束,怎么增减引用数?

2 Android智能指针原理

Android设计了基类RefBase,用以管理引用数,所有类必须从RefBase派生,RefBase是所有对象的始祖。

设计模板类sp、wp,用以引用实际对象,sp强引用和wp弱引用。sp、wp声明为栈对象,作用域结束时,自动释放,自动调用析构函数。因此,可以在sp、wp的构造函数中,增引用数;在析构函数中,减少引用计数。

专门设计weakref_impl类,该类是RefBase的内部类,用来做真正引用数管理,创建实际对象时,同时创建一个mRefs对象。不管是强引用和弱应用,都由mRefs来管理。

图2-1 展示Android智能指针的关系类图。


图 2-1 Android智能指针关系图

看了智能指针的实现原理,我们来看看具体的实现是什么样的。

2 智能指针的实现

根据前面的原理,Android设计了强引用sp和弱引用wp,故实际对象的释放,可分为强引用控制和弱引用控制。所谓强引用控制,指的是强引用数mStrong为0时,释放实际对象;弱引用控制,则指的是弱引用数mWeak为0时,才释放实际对象。

下面将结合一些实例,分析具体的实现。我们先来看一段代码实例。

代码实例1

class Sheep: public RefBase { // 羊年,定义Sheep从RefBase派生
public:
    Sheep() : RefBase() { }// 可显示调用RefBase的构造,也可以不用
    virtual ~Sheep() { }// 最好声明为virtual,以便从Sheep派生
}; 
void testSheep() {
    Sheep* pSheep = new Sheep(); // new一个Sheep对象,这个是一个堆对象
    { // 限定sp的作用域
        sp<Sheep> spSheep(pSheep); // spSheep是一个栈对象
        { // 限定wp的作用域
            wp<Sheep> wpSheep(pSheep);
        } // 调用wp的析构函数
    } // 调用sp的析构函数,实际对象pSheep已释放,若再使用pSheep将会出错
}

3.1 RefBase构造和mRefs

在实例代码中,我们先定义了一个类Sheep,从RefBase派生,创建了一个实际对象,pSheep 指向实际对象。

在构造Sheep的实际对象时,将调RefBase的构造函数。RefBase的构造函数如下,在构造函数中创建mRefs。

weakref_impl从weakref_type派生,mRefs才是真正的“管家”。
[system/core/libutils/RefBase.cpp]

RefBase::RefBase()
    : mRefs(new weakref_impl(this)) // 真正管理引用计数
{}
weakref_impl(RefBase* base)
    : mStrong(INITIAL_STRONG_VALUE) // 1<<28(268435456),为什么不是0?
    , mWeak(0)
    , mBase(base) // mBase指向实际对象
    , mFlags(0) // 这个标识很重要,指定是强应用控制还是弱引用控制
{}

请注意这里的mFlags,默认值为0,可通过修改这个标志来设置是强引用控制,还是弱引用控制,代码如下:

system/core/include/utils/RefBase.h]

    enum {

        OBJECT_LIFETIME_STRONG  = 0x0000,

        OBJECT_LIFETIME_WEAK    = 0x0001,

        OBJECT_LIFETIME_MASK    = 0x0001

    };

mFlags默认为0,即OBJECT_LIFETIME_STRONG,强引用控制。设置为OBJECT_LIFETIME_WEAK时,为弱引用控制。可以通过extendObjectLifetime函数修改,代码如下:

[system/core/libutils/RefBase.cpp]

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

3.2 sp构造

接下来,我们创建了一个sp对象spSheep,这是一个栈对象,在其作用域结束后将自动释放,调用sp的析构函数。

[system/core/include/utils/StrongPointer.h]

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}

other指向真正的Sheep对象,在sp的构造函数中,将other赋值给了sp的m_ptr,m-ptr就指向了真正的Sheep对象。

因而,other->incStrong(this),实际就是Sheep的父类RefBase的incStrong函数,代码如下:

[system/core/libutils/RefBase.cpp]

void RefBase::incStrong(const void* id) const

{

    weakref_impl* const refs = mRefs;

    refs->incWeak(id); // 调用incWeak函数

    refs->addStrongRef(id); // 由DEBUG_REFS控制,release版本什么也不做

    const int32_t c = android_atomic_inc(&refs->mStrong); // 强引用数+1,c为旧值

    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);

    if (c != INITIAL_STRONG_VALUE)  { //判断是否是第一次引用

        return;

    } // 第一次引用,refs->mStrong为1<<28 +1 (268435457)

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); 

    // refs->mStrong为1

    refs->mBase->onFirstRef(); //第一次引用时调用

}

在incStrong函数中调用refs 的incWeak函数,incWeak的代码如下:

[system/core/libutils/RefBase.cpp]

void RefBase::weakref_type::incWeak(const void* id)

{

    weakref_impl* const impl = static_cast<weakref_impl*>(this);

    impl->addWeakRef(id); // 由DEBUG_REFS控制,release版本什么也不做

    const int32_t c __unused = android_atomic_inc(&impl->mWeak); //弱引用数+1

    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);

}

OK, sp构造完成,增加一次强引用。sp构造完成后,mRefs的强引用数变为1,弱引用数也变为1;第一次强引用时,回调onFirstRef()。

3.3 wp构造

接下来,我们创建了一个wp对象wpSheep,这是一个栈对象,在其作用域结束后将自动释放,调用wp的析构函数。

[system/core/include/utils/RefBase.h]

template<typename T>

wp<T>::wp(T* other)

    : m_ptr(other)

{

    if (other) m_refs = other->createWeak(this);

}

other指向真正的Sheep对象,在wp的构造函数中,将other赋值给了wp的m_ptr,m-ptr就指向了真正的Sheep对象。

因而,other-> createWeak (this),实际就是Sheep的父类RefBase的createWeak函数,代码如下:

[system/core/libutils/RefBase.cpp]

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id); // incWeak函数前面分析过,最终的结果就是弱引用数+1
    return mRefs;
}

createWeak时,调用incWeak,最终的影响是弱引用数+1。现在,我们的实例中,强引用数为1,弱引用数为2。

返回值为mRefs,也就是m_refs和mRefs指向同一个weakref_impl对象,而mRefs的mBase指向真正的对象Sheep。因此此处的spSheep和wpSheep都是管理同一个真正的对象。

3.4 wp析构

继续看我们的实例代码,现在wpSheep的作用域结束,将调wp的析构函数,wp析构函数的代码如下:

[system/core/include/utils/RefBase.h]

template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this); // 调用decWeak函数
}

在wp的析构函数中调用m_refs的decWeak函数。m_refs和mRef指向同一个weakref_impl对象,decWeak代码如下:

[system/core/libutils/RefBase.cpp]

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,c为旧值

   ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);

   if (c != 1) return; //c为旧值,判断是否是最后一次弱引用

   // 记得前面我们说的,mFlags为0,我们并没有改变它

   if ((impl->mFlags&OBJECT_LIFETIME_WEAK) ==

           OBJECT_LIFETIME_STRONG) {

// 强引用控制,是否释放实际对象是根据强引用数

       if (impl->mStrong == INITIAL_STRONG_VALUE) {

delete impl->mBase; // 根本就没有强引用引用实际对象,释放实际对象

       } else {

           delete impl; // 释放mRefs

       }

   } else {

       impl->mBase->onLastWeakRef(id); //最后一次弱引用时调用

       if ((impl->mFlags&OBJECT_LIFETIME_MASK) ==

                       OBJECT_LIFETIME_WEAK) {

           delete impl->mBase; //弱引用控制,释放实际对象

       }

   }
}

wp析构,情况比较复杂,总的说来做了以下几件事:

Ø 弱引用数减1。

Ø 最后一次弱引用时,强引用控制,释放mRefs,若没有强引用,释放实际对象

Ø 最后一次弱引用时,弱引用控制,释放实际对象

就我们的实例来看,此时,强引用数为1,弱引用数为1,并没有任何释放。

3.5 sp析构

在我们的实例代码中,wp析构完后,sp的作用域也就结束了。此时,会调用sp的析构函数,代码如下:

[system/core/include/utils/StrongPointer.h]

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

在析构函数中调用m_ptr的decStrong函数,m_ptr指向实际对象。此处为Sheep的父类RefBase的decStrong函数,代码如下:

[system/core/libutils/RefBase.cpp]

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id); // 由DEBUG_REFS控制,release版本什么也不做
    const int32_t c = android_atomic_dec(&refs->mStrong); // 强引用数-1
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) { // c为旧值,c为1时,即强引用数为0
        refs->mBase->onLastStrongRef(id); //最后一次强引用结束时调用
        if ((refs->mFlags&OBJECT_LIFETIME_MASK) ==
                        OBJECT_LIFETIME_STRONG) {
            delete this; // 若是强引用控制,释放实际对象,调实际对象的析构函数
        }
    }
    refs->decWeak(id);
}

refs的decWeak函数,前面wp析构的时候分析过,这里不再重复。sp析构完成,主要完成以下工作:

Ø 强引用数减1,弱引用数据减1。

Ø 最后一次强引用时,若是强引用控制,释放实际对象,释放mRefs,调用onLastStrongRef函数。

在我们的代码中,此时强引用数为0,弱引用数为0,实际对象的析构函数将被调用,mRefs将被释放。下面我看看实际对象的析构函数。

3.6 RefBase析构

实际对象的析构,先析构RefBase,RefBase的析构函数如下:

[system/core/libutils/RefBase.cpp]

RefBase::~RefBase()
{
    if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
        delete mRefs; // 没有强引用引用实际对象,释放mRefs
    } else {
        if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) !=
                        OBJECT_LIFETIME_STRONG) {
            if (mRefs->mWeak == 0) {
                delete mRefs; // 释放mRefs指向的对象
            }
        }
    }
    // for debugging purposes, clear this.
    const_cast<weakref_impl*&>(mRefs) = NULL; // mRefs指向0,避免野指针
}

OK,RefBase析构分析完了,在RefBase的析构函数中主要的工作就是释放mRefs指向的weakref_impl的对象。

到此,我们的实例代码分析完成,我们首先构造一个Sheep对象,pSheep指向实际对象。再分别构造一个强引用sp和一个弱引用wp,用以引用实际对象,实际对象的释放就由sp和wp控制,我们并没有显示的释放构造的pSheep指向的实际对象。

我们来看看实例代码1中,对象的构造和析构Log:

[示例代码1的Log]

D/        (13624): Sheep::------------------testSheep start--------------------------
D/        (13624): Sheep::Sheep constructor invoked this=0xb6301080
D/        (13624): Sheep:: No refs, strong count=268435456, weak count=0
D/        (13624): Sheep::in sp scope ------------
D/        (13624): Sheep::onFirstRef, object=0xb6301080
D/        (13624): Sheep:: After strong ref, strong count=1, weak count=1
D/        (13624): Sheep::in wp scope ------------
D/        (13624): Sheep:: After weak ref, strong count=1, weak count=2
D/        (13624): Sheep::out wp scope ------------
D/        (13624): Sheep:: release weak ref, strong count=1, weak count=1
D/        (13624): Sheep::out sp scope ------------
D/        (13624): Sheep::onLastStrongRef, id=0xbec42884
D/        (13624): Sheep::Sheep destructor invoked this=0xb6301080
D/        (13624): Sheep::--------------------testSheep end--------------------------

3.7 实际对象的状态

通过前面的分析,我们可以绘制出实际对象的状态图,如下如所示:

图 3-1 实际对象的状态图

4 智能指针的使用

前面我们通过示例代码1,知道了智能指针是怎么管理实际对象的,怎么控制实际对象的释放的。但是我们只是分析了其中的构造函数和析构函数,下面我们将对智能指针做全面的了解。

4.1 RefBase的特性

我们先看看RefBase的类图,如图4-1所示。

图 4-1 RefBase类图
  • 所有类须从RefBase派生,只有一个无参构造函数,RefBase析构函数需申明为virtual。
  • 在构造函数中创建mRefs对象,为weakref_impl类型。
  • 可以在派生类中通过函数extendObjectLifetime指定是强引用控制,还是弱引用控制,默认为强引用控制。
  • 在析构函数中,判断是否释放mRefs。
  • 私有的构造函数和赋值运算重载,不允许子类使用。
  • 获取实际对象的强引用数getStrongCount
  • 子类可派生virtual成员函数,获知自身的引用情况。

[system/core/include/utils/StrongPointer.h]

// 第一次强引用时回调
virtual void onFirstRef();
// 最后一次强引用时调用
virtual void onLastStrongRef(const void* id); 
//由弱到强时调用,稍候介绍
virtual bool onIncStrongAttempted(uint32_t flags, const void* id); 
// 最后一次弱引用时调用
virtual void onLastWeakRef(const void* id); 

mRefs指向一个weakref_impl对象,是RefBase的应用计数管家,其类图如下图4-2:

图 4-2 mRefs的类图
  • 可以通过getWeakRefs()->getStrongCount()获取实际对象的弱引用数

4.2 sp模板类的特性

图 4-3 sp的类图
  • 提供多种形式的构造方式
  • 定义多种形式的赋值运算操作
  • 重载操作运算符*,可以获取实际对象
  • 重载操作运算符->,可以获取指向实际对象的指针
  • 可通过get函数,获取实际对象的指针
  • force_set函数可以指定sp引用的实际对象,该函数设计有点缺点,若sp当前已经引用其他的对象,则可能造成其他对象无法释放。稍后我们单独介绍。

4.3 wp模板类特性

图 4-4 wp类图
  • 提供多种形式的构造方式
  • 定义多种形式的赋值运算操作
  • 可通过unsafe_get函数,获取实际对象的指针,但是可能获取到的是空的或是野指针
  • 可以通过promote函数将弱引用变为强引用,这个是一个比较重要的函数,我们通过一个实例来看看是怎么由弱变强的。

[实例代码2]

void testPromote() {
  {
    Sheep* pSheep = new Sheep();
    wp<Sheep> wpSheep(pSheep);
    sp<Sheep> spSheep = wpSheep.promote();
    }
}

Promote函数如下:

[system/core/include/utils/RefBase.h]

template<typename T>
sp<T> wp<T>::promote() const
{
    sp<T> result;
    if (m_ptr && m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);
    }
    return result;
}

[system/core/libutils/RefBase.cpp]

bool RefBase::weakref_type::attemptIncStrong(const void* id)

{

    incWeak(id); // 前面分析过,弱引用数+1;我们的实例中,此时弱引用数为2

    weakref_impl* const impl = static_cast<weakref_impl*>(this);

    int32_t curCount = impl->mStrong;

    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) {

        if ((impl->mFlags&OBJECT_LIFETIME_WEAK) ==

                        OBJECT_LIFETIME_STRONG) {

                … …

            while (curCount > 0) {

                if (android_atomic_cmpxchg(curCount, curCount + 1,

                        &impl->mStrong) == 0) { // 强引用控制,强引用数+1

                    break;

                }

                curCount = impl->mStrong;

            }

             … …

        } else {

            … … // 弱引用控制,强引用数 +1

            curCount = android_atomic_inc(&impl->mStrong); 

        }

    }

    … …

    return true; // 由弱变强成功

}

promote函数成功后,强引用数+1,弱引用数+1。在我们的实例3中,此时弱引用数为2,强引用数为1。我们来看一下Log:

[实例代码2的Log信息]

D/        (13742): Sheep:: -------------testPromote begin------------
D/        (13742): Sheep::Sheep constructor invoked this=0xb6301080
D/        (13742): Sheep:: No refs, strong count=268435456, weak count=0
D/        (13742): Sheep:: After weak ref, strong count=268435456, weak count=1
D/        (13742): Sheep:: After promote, strong count=1, weak count=2
D/        (13742): Sheep::onLastStrongRef, id=0xbed1c884
D/        (13742): Sheep::Sheep destructor invoked this=0xb6301080
D/        (13742): Sheep:: -------------testPromote   end------------

为什么要由弱生强?我们通过弱指针wp,不能获取实际的对象,wp并没有提供sp那个的存取操作*和->的重载,由弱生强后,可以sp获取实际的对象。

5 轻引用 LightRefBase

前面介绍的RefBase和mRefs比较复杂,Android还提供了一个轻型的引用管理LightRefBase。LightRefBase的代码比较少,直接看代码:

[system/core/include/utils/RefBase.h]

template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(__attribute__((unused)) const void* id) const {
        android_atomic_inc(&mCount);
    }

    inline void decStrong(__attribute__((unused)) 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的类图如下:

图 5-1 LightRefBase类图

LightRefBase比较简单,直接用mCount控制引用数,从LightRefBase的特性看来,LightRefBase只支持sp控制,不支持wp。

下面通过一个实例代码,看看LightRefBase的使用,代码如下:

[实例代码3]

class Goat: public LightRefBase<Goat> {

public:

Goat() {

ALOGD("Goat::Goat constructor invoked this=%p \n", this);

}

private:

friend class LightRefBase<Goat> ;

~Goat() {

ALOGD("Goat::Goat destructor invoked this=%p \n", this);

}

};

void testGoat() {

Goat* pGoat = new Goat(); //初始时mCount为0

ALOGD("Goat:: before sp ref, mCount=%d", pGoat->getStrongCount());

{

sp<Goat> spGoat(pGoat); // 调用pGoat的incStrong函数,mCount为1

}

// spGoat的作用域结束,调用pGoat的decStrong函数,mCount为0,delete掉pGoat

}

实例代码也非常简单,只是和羊杆上了…今年羊年。下面是Goat的构造和析构情况,情况Log。

[实例代码3的Log信息]

D/        (14539): Goat::--------------------testGoat end--------------------------
D/        (14539): Goat::Goat constructor invoked this=0xb6301080
D/        (14539): Goat:: before sp ref, mCount=0
D/        (14539): Sheep::in sp scope ------------
D/        (14539): Goat:: After sp ref, mCount=1
D/        (14539): Sheep::out sp scope ------------
D/        (14539): Goat::Goat destructor invoked this=0xb6301080
D/        (14539): Goat::--------------------testGoat end--------------------------

注意,LightRefBase的析构函数不是virtual的,对象释放时,可能会造成子类的析构函数调不到,因此,Android对LightRefBase做了一个简单的包裹,提供了VirtualLightRefBase类,VirtualLightRefBase的析构函数是virtual的,使用时,优先使用VirtualLightRefBase。

6 Android原始设计缺陷

任何设计都不敢保证没有缺陷。前面说sp的时候我们说到sp的force_set函数,下面我将具体来分析。

6.1 **force_set会破坏 sp 设计的初衷

force_set函数的原型:

[system/core/include/utils/StrongPointer.h]

template<typename T>
void sp<T>::force_set(T* other) {
    other->forceIncStrong(this);
    m_ptr = other; // 若m-ptr之前指向其他的对象,则其他对象的m-ptr不为0
}

我们来举例说明一下,请看示例代码4:

[实例代码4]

void testForceSet()
{
  {
    sp<Sheep> spSheep(new Sheep());
    spSheep.force_set(new Sheep ());
  }
}

我们看看Sheep的释放情况:

[实例代码4的Log信息]

D/        (13385): Sheep:: -------------testForceSet begin------------
D/        (13385): Sheep::Sheep constructor invoked this=0xb6301080
D/        (13385): Sheep::onFirstRef, object=0xb6301080
D/        (13385): Sheep::Sheep constructor invoked this=0xb6301088
D/        (13385): Sheep::onFirstRef, object=0xb6301088
D/        (13385): Sheep::onLastStrongRef, id=0xbea5a894
D/        (13385): Sheep::Sheep destructor invoked this=0xb6301088
D/        (13385): Sheep:: -------------testForceSet begin------------

可见,我们构造了两个对象0xb6301080和0xb6301088,但是只有0xb6301088释放掉了,0xb6301080没有并释放。所以使用force_set函数时一定要注意,但是如下的代码是没有问题的。

[演示代码3]

void testForceSet()
{
  {
    sp<Sheep> spSheep; // spSheep的m_ptr并没有指向实际的对象
    spSheep.force_set(new Sheep ());
  }
}

6.2 force_set的改进方案

在force_set函数时,先将原引用的对象的引用数-1,sp再引用新对象,这这样可避免原实际对象不能释放的问题。

[force_set的改进方案]

--- a/include/utils/StrongPointer.h
+++ b/include/utils/StrongPointer.h
@@ -187,7 +187,12 @@ sp<T>& sp<T>::operator =(U* other) {
 template<typename T>
 void sp<T>::force_set(T* other) {
-    other->forceIncStrong(this);
+    if (m_ptr) {
+        m_ptr->decStrong(this);
+    }
+    if (other) {
+        other->forceIncStrong(this);
+    }
     m_ptr = other;
 }

7 智能指针小结

我们从C++的特性出发,逐步展开Android中智能指针的相关知识,从原理,到实现,再到具体的使用。
Android设计sp和wp对实际对象进行引用,而实际对象类需要从基类RefBase或LightRefBase派生。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 144,247评论 1 305
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,830评论 1 258
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 95,531评论 0 214
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,345评论 0 183
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 49,160评论 1 260
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,936评论 1 178
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,538评论 2 275
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,291评论 0 168
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 29,162评论 6 237
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,654评论 0 214
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,401评论 2 217
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,747评论 1 232
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,297评论 1 33
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,206评论 2 213
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,670评论 3 213
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,661评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,089评论 0 169
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,677评论 2 233
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,819评论 2 237

推荐阅读更多精彩内容

  • 指针 在传统的C++编程中,指针的使用一直是一把双刃剑。指针赋予了我们直接操作硬件地址的能力,但同时也带来了诸多问...
    passerbywhu阅读 2,653评论 0 2
  • 本文主要内容 进程通信概念 智能指针概念 强指针 弱指针 进程通信概念 同一个程序中两个函数能互相调用的根本原因是...
    某昆阅读 892评论 0 5
  • 引言:由于未来需要深入android底层进行系统级别的开发,所以最近在看老罗的《Android系统源代码情景分析》...
    拿破轮阅读 2,141评论 0 9
  • Java和C/C++的一个重大区别,就是它没有"指针"的概念,这并不代表Java不需要指针,而是将这个"超级武器隐...
    Sophia_dd35阅读 453评论 0 1
  • 前言 Java 和 C/C++ 的一个重大区别,就是它没有"指针"的概念,这并不代表 Java 不需要只用指针,而...
    seraphzxz阅读 2,372评论 0 54