Android智能指针——读书笔记

目录结构


参考资料

在写这篇博客前,我也是读了罗升阳的《Android系统源代码情景分析》和邓凡平《深入理解Android系统》。说实话,单就智能指针这一章节,邓凡平的书确实差了罗升阳的一大截。因此,我建议想要透彻搞懂Android的智能指针,还是把罗升阳的《Android系统源代码情景分析》中智能指针章节通读一遍比较好。
本文主要就是读罗升阳《Android系统源代码情景分析》——智能指针章节的读书笔记。


概述

在Android系统的应用程序框架层中,有相当一部分代码是使用C++语言开发的。Android团队出于性能考虑使用了C++代码,但是需要想办法避免C++经常遇到的指针问题。C++中的指针问题主要有两类:

  • 内存泄漏。

  • 无效引用。

为了尽量避免这类问题,Android系统开发了C++智能指针。


背景知识

我们通常使用引用计数技术来维护对象的生命周期。每当有一个新的指针指向了某一个对象时,这个对象的引用计数就增加1,相反,每当有一个指针不再指向一个对象,这个对象的引用计数就减少1。当对象的引用计数为0时,它所占用的内存就可以安全地释放了。这种对象引用计数技术的核心问题是由谁来维护对象的引用计数。
智能指针正是一种能够自动维护对象引用计数的技术。这里需要特别强调,智能指针是一个对象,而不是一个指针,但是它引用了一个实际使用的对象。正是因为它是一个对象,因此它能够自动地维护实际对象的引用计数。简单来说:

  • 在智能指针构造时,就增加它所引用的对象的引用计数。

  • 在智能指针析构时,就减少它所引用的对象的引用计数。

由于C++中智能指针的构造函数和析构函数都是自动执行的,因此,它就很自然的实现了自动的对象引用计数技术。


GC经典问题

上述这种简单的对象引用计数技术似乎解决了C++指针的问题,但是它存在着缺陷。考虑这样一个场景:

有两个对象A和B,对象A引用了对象B,而对象B也引用了对象A。一方面,当对象A不再使用时,就可以释放它所占用的内存了,但是由于对象B仍然引用着它,因此,此时对象A就不能被释放。另一方面,当对象B不再使用时,就可以释放它所占用的内存了,但是由于对象A仍然引用着它,因此,对象B也不能释放。

这就是GC的经典问题之一:对象A等待对象B被释放,而对象B也在等待对象A被释放。除非能够同时知道对象A和对象B都不再使用了,这个问题才能解决。然而,通常系统是不知道对象之间的依赖关系的。
为了解决这个GC的经典问题,我们就需要采取另外一种稍微复杂的引用技术来维护对象的生命周期。这种引用计数技术将对象的引用计数分为强引用计数和弱引用计数两种,其中,对象的生命周期只受强引用计数控制。在使用强引用计数和弱引用计数的解决方案中,一般将有关联的对象划分为“父-子”和“子-父”的关系。在“父-子”关系中,“父”对象通过强引用计数来引用“子”对象。而在“子-父”关系中,“子”对象通过弱引用计数来引用“父”对象。这样就可以解决由于相互引用而造成的对象不能释放的问题了。
了解了上述背景知识后,我们开始进入Android系统的智能指针原理。Android系统提供了三种类型的C++指针,分别是:

  • 轻量级指针(Light Pointer)

  • 强指针(Strong Pointer)

  • 弱指针(Weak Pointer)

其中,轻量级指针使用了简单的引用计数技术,而强指针和弱指针使用了强引用计数和弱引用计数技术。
无论是轻量级指针,还是强指针或弱指针,它们的实现原理都是类似的,即需要对象提供引用计数器,但是由智能指针来负责维护这个引用计数器。Android系统将引用计数器定义为一个公共类,所有支持使用智能指针的对象类都必须要从这个公共类继承下来。这样,Android系统的智能指针就可以通过这个引用计数器来维护对象的生命周期了。


轻量级指针

轻量级指针通过简单的引用计数技术来维护对象的生命周期。如果一个类的对象支持轻量级指针,那么它必须从LightRefBase类继承下来,因为LightRefBase类提供了一个简单的引用计数器。

实现原理分析

我们首先来看一下LightRefBase类的实现,源码位置为/system/core/include/utils/RefBase.h,源码内容如下:

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_case<const T*>(this);
        }
    }
    // ! DEBUGGING ONLY: Get current strong ref count.
    inline int32_t getStrongCount() const {
        return mCount;
    }
protected:
    inline ~LightRefBase() {}
private:
    mutable volatile int32_t mCount;
}

LightRefBase类只有一个成员变量mCount,用来描述一个对象的引用计数值。LightRefBase类同时提供了成员函数incStrong和decStrong来增加和减少它所引用的对象的引用计数。

注意

在成员函数decStrong中,如果对象的引用计数值在减少之后变成0,那么就表示需要释放这个对象所占用的内存了。

轻量级指针的实现类为sp,它同时也是强指针的实现类。sp类的源码位置在:/system/core/include/utils/StrongPointer.h,这里只关注sp跟轻量级指针相关的实现,源码定义如下:

template <typename T>
class sp
{
public:
    typedef typename RefBase::weakref_type weakref_type;
    inline sp() : m_ptr(0) {}
    sp(T* other);
    sp(const sp<T>& other);
    ~sp();
private:
    T* m_ptr;
}

sp类是一个模板类。其中,模板参数T表示对象的实际类型,它也是必须继承了LightRefBase类的。
sp类的实现比较复杂,但是与轻量级指针相关的部分只有成员变量m_ptr以及构造函数和析构函数。在sp类中,成员变量m_ptr是一个指针,它是在构造函数里面初始化的,指向实际引用的对象。接下来,我们分析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);
}

在这两个构造函数里面,首先都是初始化成员变量m_ptr,然后再调用它的成员函数incStrong来增加它的引用计数。由于成员变量m_ptr所指向的对象是从LightRefBase类继承下来的,因此,这两个构造函数实际上是调用了LightRefBase类的成员函数incStrong来增加对象的引用计数。


析构函数

sp类的析构函数的实现如下所示:

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

sp类的析构函数执行的操作刚好与构造函数相反,即调用成员变量m_ptr所指向的对象的成员函数decStrong来减少它的引用计数,实际上是调用LightRefBase类的成员函数decStrong来减少对象的引用计数。


应用实例分析

我在应用目录下建立了一个C++程序lightpointer来说明轻量级指针的使用方法。应用程序lightpointer的实现很简单,只有一个lightpointer.cpp和一个编译脚本文件Android.mk。下面我们就分别介绍它们的内容。

  • lightpointer.cpp
#include <stdio.h>
#include <utils/RefBase.h>

using namespace android;

class LightClass : public LightRefBase<LightClass>
{
public:
    LightClass()
    {
        printf("Construct LightClass Object.");
    }

    virtual ~LightClass()
    {
        printf("Destory LightClass Object.");
    }
};

int main(int argc, char** argv)
{
    LightClass* pLightClass = new LightClass();
    sp<LightClass> lpOut = pLightClass;

    printf("Light Ref Count:%d.\n", pLightClass->getStrongCount());

    {
        sp<LightClass> lpInner = lpOut;
        printf("Light Ref Count:%d.\n", pLightClass->getStrongCount());
    }

    printf("Light Ref Count:%d.\n", pLightClass->getStrongCount());

    return 0;
}

我们来分析程序的执行过程:

  1. lightpointer.cpp创建了一个继承自LightRefBase类的LightClass类,因此,LightClass类的对象能够结合轻量级指针来使用。
  2. 在应用程序lightpointer的入口函数main中,首先创建了一个LightClass对象pLightClass,然后再创建一个轻量级指针lpOut来引用它。在轻量级指针lpOut的创建过程中,会调用sp类的构造函数来增加LightClass对象pLightClass的引用计数,即此时LightClass对象pLightClass的引用计数值为1,因此第一条printf语句的打印值为1。
  3. 接下来,我们在一个内嵌的作用域中创建了另外一个轻量级指针lpInner来引用LightCLass对象pLightClass,此时,LightClass的对象pLightClass的引用计数值会再增加1,因此,第二条printf语句的打印值为2。
  4. 当应用程序执行到内嵌作用域外后,lpInner轻量级指针就被析构了,这时候LightClass对象pLightClass的引用计数值就会减少1,因此,最后一条printf语句的打印值应该是1。
  5. 当程序执行结束后,lpOut的析构函数也会被调用,pLightClass对象的引用计数还会减1变为0,于是触发了LightRefBase类decStrong方法中delete条件,pLightClass对象被释放。

为了在Android源码中编译lightpointer这个二进制程序,还需要提供Android.mk,Android.mk代码实现如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := lightpointer
LOCAL_SRC_FILES := lightpointer.cpp
LOCAL_SHARED_LIBRARIES := \
    libcutils \
    libutils

include $(BUILD_EXECUTABLE)

我们把编译出来的lightpointer文件push到手机的/system/bin目录下,然后手动执行一下,结果如下:
lightpointer执行结果
上面的运行结果和我们的分析完全一致,证明我们对轻量级指针的实现原理的分析是正确的。


强指针和弱指针

强指针和弱指针通过强引用计数和弱引用计数来维护对象的生命周期。如果一个类的对象要支持强指针和弱指针,那么它就必须从RefBase类继承下来,因为RefBase类提供了强引用计数器和弱引用计数器。


强指针的实现原理分析

我们首先来分析一下RefBase类的实现原理,源码位置为/system/core/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);
        bool attemptIncStrong(const void* id);

        //! This is only safe if you have set OBJECT_LIFTTIME_FOREVER
        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.
        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);
    }

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:
    firend class weakref_type;
    class weakref_impl;

    RefBase(const RefBase& o);
    RefBase& operator = (const RefBase& o);

    weakref_impl* const mRefs;
}

与LightRefBase类一样,RefBase类也提供了成员函数incStrong和decStrong来维护它所引用的对象的引用计数。不过,RefBase类与LightRefBase类不一样的是,它不是直接使用一个整数来维护对象的引用计数的,而是使用一个weakref_impl对象,及成员变量mRefs来描述对象的引用计数。
weakref_impl类同时为对象提供了强引用计数和弱引用计数,它的源码如下所示(/system/core/libutils/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)
    {
    }

    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) {}
#else
    // ......
#endif
}

weakref_impl类继承了weakref_type类。weakref_type类定义在RefBase类的内部,它提供了成员函数incWeak、decWeak、attemptIncStrong和attemptIncWeak来维护对象的强引用计数和弱引用计数。weakref_type类只定义了引用计数维护接口,具体的实现是由weakref_impl类提供的。
weakref_impl类有两个成员变量mStrong和mWeak,分别用了描述对象的强引用计数和弱引用计数。同时,weakref_impl类的成员变量mBase指向了它所引用的对象的地址,而成员变量mFlags是一个标志值,用了描述对象的生命周期控制方式。
weakref_impl类的成员变量mFlags的取值范围为0和OBJECT_LIEFTIME_WEAK。其中:

  • 0:表示对象的生命周期只受强引用计数的影响。

  • OBJECT_LIFETIME_WEAK:表示对象的生命周期同时受强引用计数和弱引用计数影响 。

下面,我们通过一个类图来总结RefBase、weakref_type和weakref_impl三个类的关系,如下图所示:
智能指针-强指针UML
从这个类图可以看出,每一个RefBase对象都包含一个weakref_impl对象,而后者继承了weakref_type类。接下来我们会介绍这三个类的作用及其关系。
强指针的实现类为sp,前面我们只分析了轻量级指针的实现。在本节中,我们将分析sp类的强指针实现,主要是分析它的构造函数和析构函数的实现。sp类的构造函数源码实现如下:

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

注意:
模板参数T是一个继承了RefBase的子类,因此,调用other->incStrong(this)其实是调用了RefBase类的成员函数incStrong来增加对象的强引用计数。

RefBase类的incStrong源码实现如下:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->addWeakRef(id);
    refs->incWeak(id);
    refs->addStrongRef(id);
    const int32_t c = android_atomic_inc(&refs->mStrong);
    if (c != INITIAL_STRONG_VALUE) {
        return;
    }
    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    const_cast<RefBase*>(this)->onFirstRef();
}

RefBase类的成员变量mRefs是在构造函数中初始化的,源码如下所示:

RefBase::RefBase()
    :mRefs(new weakref_impl(this))
{
    // LOGV("Creating refs %p with RefBase %p\n", mRefs, this);
}

回到RefBase类的成员函数incStrong中,它主要做了三件事情:
1. 调用refs->incWeak(id)来增加对象的弱引用计数。
2. 调用const int32_t c = android_atomic_inc(&refs->mStrong);来增加对象的强引用计数。
3. 如果c == INITIAL_STRONG_VALUE说明该对象是第一次被强指针引用,则第20行调用对象的成员函数onFirstRef来通知对象。RefBase类的成员函数onFirstRef是一个空实现,如果子类想要处理这个事件,那么需要重写这个方法。

下面,我们分别来分析这三件事情的源码实现。


增加对象的弱引用计数

增加对象的弱引用计数是通过调用RefBase类的成员变量mRefs的成员函数incWeak实现的。RefBase类的成员变量mRefs的类型为weakref_impl,它的成员函数incWeak是从父类weakref_type继承下来的,因此,它实际上是通过调用weakref_type类的成员函数incWeak来增加对象的弱引用计数的,源码如下所示:

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    const int32_t c = android_atomic_inc(&impl->mWeak);
}

this指针指向的实际上是一个weakref_impl对象,因此,首先通过代码weakref_impl* const impl = static_cast<weakref_impl*>(this);将this转换为一个weakref_impl指针。有了这个impl指针,我们就可以通过android_atomic_inc方法增加impl的成员变量mWeak的值,即增加对象的弱引用计数。


增加对象的强引用计数

增加对象的强引用计数是通过代码const int32_t c = android_atomic_inc(&refs->mStrong);实现的,即增加了mRefs的成员变量mStrong的值。函数android_atomic_inc返回的是对象尚未+1前的强引用计数值。在weakref_impl类的构造函数中,成员变量mStrong的值被初始化为INITIAL_STRONG_VALUE。INITIAL_STRONG_VALUE是一个宏,它的定义如下所示:

#define INITIAL_STRONG_VALUE (1 << 28)

从理论上说,当对象第一次被强指针引用时,它的强引用计数值应该等于1。但是我们看到,对象的强引用计数的初始值为INITIAL_STRONG_VALUE,它加1之后并不等于1,因此,RefBase类的成员函数incStrong需要将它调整为1,关键代码如下所示:

if (c != INITIAL_STRONG_VALUE) {
    return;
}
android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);

至此,强指针类sp的构造函数的实现就分析完了,它主要做的事情就是增加对象的强引用计数和弱引用计数。从上面的代码分析我们就可以看出,虽然我们的目的是增加对象的强引用计数,但是同时也会增加对象的弱引用计数,即一个对象的弱引用计数一定是大于或者等于它的强引用计数的。


sp的析构函数实现

sp类的析构函数源码实现如下所示:

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

前面提到,sp类的成员变量m_ptr所指向的对象就是继承了RefBase类的,因此,第4行实际上是调用了RefBase类的成员函数decStrong来减少对象的强引用计数,它的实现如下所示:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    const int32_t c = android_atomic_dec(&refs->mStrong);
    if (c == 1) {
        const_cast<RefBase*>(this)->onLastStrongRef(id);
        if ((refs->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
            delete this;
        }
    }
    refs->decWeak(id);
}

sp类的析构函数执行的操作刚好与构造函数相反,它主要是减少对象的强引用计数和弱引用计数。
代码中通过调用函数android_atomic_dec来减少对象的强引用计数。与函数android_atomic_inc类似,函数android_atomic_dec返回的值是对象原来的强引用计数值,即减1前的值,保存在变量c中。如果变量c的值等于1,说明此时再也没有强指针引用这个对象了,因此,接下来就调用该对象的成员函数onLastStrongRef来使得它可以执行一些业务相关的逻辑,同时也要考虑是否需要释放该对象。
当对象的强引用计数为0时,还需要判断该对象是否受弱引用计数控制,即RefBase类的成员变量mRefs的标志值mFlags的OBJECT_LIFETIME_WEAK位是否为1。如果不受弱引用指针的影响,接下来就会释放对象所占用的内存,这同时会导致RefBase类的析构函数被调用。RefBase的析构函数源码如下:

RefBase::~RefBase()
{
    if (mRefs->mWeak == 0) {
        delete mRefs;
    }
}

在RefBase类的析构函数中,如果发现对象的弱引用计数值为0,那么就会把引用对象mRefs也一起释放。RefBase类的成员变量mRefs指向的是一个weakref_impl对象,它是在RefBase类的构造函数中创建的。现在既然它所属的RefBase对象已经不存在了,并且它所引用的对象的弱引用计数值也等于0,那它也就不需要存在了。前面提到,一个对象的弱引用计数一定是大于等于它的强引用计数的,因此,当一个对象的强引用计数为0时,它的弱引用计数值可能大于0。因此,当对象的强引用计数为0,弱引用计数大于0时,我们只能将对应的RefBase对象释放掉,而不能将该RefBase对象内部的weakref_impl对象也释放掉,因为还有其他的弱指针通过该weakref_impl对象来引用实际的对象。只有当对象的弱引用计数值也为0时,才可以将该weakref_impl对象一起释放掉。
回到RefBase类的成员函数decStrong中,refs->decWeak(id);代码执行减少对象的弱引用计数的操作。变量refs指向的是RefBase类内部的weakref_impl对象mRefs。weakref_impl类的成员函数decWeak是从父类weakref_type类继承下来的,因此,接下来实际执行的是weakref_type类的成员函数decWeak,源码实现如下所示:

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    const int32_t c = android_atomic_dec(&impl->mWeak);
    if (c != 1) {
        return;
    }

    if ((impl->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
        if (impl->mStrong == INITIAL_STRONG_VALUE) {
            delete impl->mBase;
        } else {
            // Freeing refs of old RefBase
            delete impl;
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags & OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
            delete impl->mBase;
        }
    }
}

代码中首先调用android_atomic_dec函数来减少对象的弱引用计数,并且返回减少之前的值,保存在变量c中。如果c的值不等于1,那么说明还有其他的弱引用指针在引用这个对象,因此,就不用进一步处理了。如果c的值等于1,那么就说明再也没有弱指针引用这个对象了,同时也说明没有强指针引用这个对象了,因为当对象的弱引用计数值等于0时,它的强引用计数值也一定等于0。在对象的弱引用计数等于0时,我们就要考虑是否需要将该对象释放掉。这取决于对象的生命周期控制方式,以及该对象是否被强指针引用过。下面我们分为两种情况来考虑:

  • 第一种情况是对象的生命周期只受强引用计数控制,即impl->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK结果为true。此时,如果impl->mStrong == INITIAL_STRONG_VALUE也成立,则代表当前的RefBase对象没有被强指针引用过,则接下来可以直接将该RefBase对象释放掉。如果对象的生命周期只受强引用计数控制,并且它也被强指针引用过,那么在该对象的弱引用计数值变为0时,该RefBase对象就已经在RefBase类的成员函数decStrong中被释放掉了,因此,else中只负责释放其内部的引用计数器对象weakref_impl即可。

  • 第二种情况是对象的生命周期受弱引用计数控制,即impl->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK为false。首先,调用对象的成员函数onLastWeakRef来使得它可以执行一些业务相关的逻辑,同时也要考虑是否需要将该对象释放掉。判断对象的生命周期是否完全不受强弱引用计数控制,即判断impl->mFlags & OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER,如果impl->mFlags的OBJECT_LIFETIME_FOREVER标志位不为1,那么接下来就会释放掉对象所占用的内存。


小结

这里我们对对象的生命周期控制方式做一个小结。

  1. 如果一个对象的生命周期控制标志值被设置为0,那么只要它的强引用计数值为0,系统就会自动释放这个对象。
  2. 如果 一个对象的生命周期控制标志值被设置为OBJECT_LIFETIME_WEAK,那么只有当它的强引用计数值和弱引用计数值都为0时,系统才会释放掉这个对象。

弱指针的实现原理分析

如果一个类的对象支持使用弱指针,那么这个类就必须要从RefBase类继承下来,因为RefBase类提供了弱引用计数器。前面我们已经分析了RefBase类的实现了,因此,这里只分析弱指针类wp的实现。wp类的源码位置:/system/core/libutils/RefBase.cpp,wp类的源码实现如下:

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(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;
    }

    // Operatprs
    COMPARE_WEAK(==)
    COMPARE_WEAK(!=)
    COMPARE_WEAK(>)
    COMPARE_WEAK(<)
    COMPARE_WEAK(<=)
    COMPARE_WEAK(>=)

    inline bool operator == (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
    }

    template<tyename U>
    inline bool operator == (const wp<U>& o) const {
        return m_ptr == o.m_ptr;
    }

    inline bool operator > (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
    }


    template<typename U>
    inline bool operator > (const wp<U>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
    } 

    inline bool operator < (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
    }

    template<typename U>
    inline bool operator < (const wp<U>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
    }

    inline bool operator != (const wp<T>& o) const {
        return m_refs != o.m_refs;
    }

    template<typename U>
    inline bool operator != (const wp<U>& o) const {
        reurn !operator == (o);
    }

    inline bool operator <= (const wp<T>& o) const {
        return !operator > (o);
    }

    template<typename U>
    inline bool operator <= (const wp<U>& o) const {
        return !operator > (o);
    }

    inline bool operator >= (const wp<T>& o) const {
        return !operator < (o);
    }

    template<typename U>
    inline bool operator >= (const wp<U>& o) const {
        return !operator < (o);
    }

private:
    template<typename Y> firend class sp;
    template<typename Y> firend class wp;

    T* m_ptr;
    weakref_type* m_refs;
}

wp类是一个模板类,其中,模板参数T表示对象的实际类型,它必须是从RefBase类继承下来的。与强指针sp类类似,弱指针类wp也有一个成员变量m_ptr,用来指向它所引用的对象,但是弱指针类wp还使用另外一个类型为weakref_type*的成员变量m_refs,用来维护对象的弱引用计数。
弱指针与强指针有一个很大的区别,就是弱指针不可以直接操作它所引用的对象,因为它所引用的对象可能是不受弱引用计数控制的,即它所引用的对象可能是一个无效的对象。因此,如果需要操作一个弱指针所引用的对象,那么就需要将这个弱指针升级为强指针,这是通过调用它的成员函数promote来实现的。如果升级成功,就说明该弱指针所引用的对象还没有被销毁,可以正常使用。
wp类的实现比较复杂,但是只要掌握了它的构造函数和析构函数的实现,以及它是如果将一个弱指针升级为强指针的,就可以理解它的实现原理了。因此,接下来我们先分析wp类的构造函数和析构函数实现,然后再分析它的成员函数promote的实现。


wp类的构造函数实现

wp类的构造函数源码如下:

template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) {
        m_refs = other->createWeak(this);
    }
}

注意:
模板参数T是一个继承了RefBase类的子类,因此,调用other->createWeak(this)方法其实是调用了RefBase类的成员函数createWeak来增加对象的弱引用计数。

RefBase类的createWeak函数实现源码如下:

RefBase::weakref_type* RefBase::crateWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

RefBase类的成员变量mRefs指向的是一个weakref_impl对象。在前面已经介绍过它的成员函数incWeak的实现了,具体实现就是增加实际对象的弱引用计数。RefBase类的成员函数createWeak最后将它的成员变量mRefs所指向的一个weakref_impl对象返回给调用者。


wp类的析构函数实现

wp类的析构函数的实现如下所示:

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

弱指针在析构时,会调用它的成员变量m_refs的成员函数decWeak来减少对象的弱引用计数。因此m_refs指向的是一个weakref_impl对象,因此这里是调用了weakref_impl类的成员函数decWeak来减少对象的弱引用计数。decWeak具体的函数实现我们已经在前面介绍过了。


wp类的promote函数实现

弱指针通过调用promote函数来升级为强指针,升级为强指针后,才能操作它所引用的对象。

wp类是如何使得一个弱指针不能直接操作它所引用的对象的呢?秘密就在于wp类没有重载*和->操作符号,因此,我们就不能直接操作它所引用的对象。

wp类的成员函数promote的实现如下所示:

template<typename T>
sp<T> wp<T>::promote() const
{
    return sp<T>(m_ptr, m_refs);
}

通过源码可以看出,弱指针升级为强指针的方式就是通过其内部的成员变量m_ptr和m_refs来创建一个强指针。源码如下:

template<typename T>
sp<T>::sp(T* p, weakref_type* refs)
    : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
{
}

参数p指向对象的地址,而参数refs指向该对象内部的一个弱引用计数器对象。只有在对象地址不为NULL的情况下,才会调用它内部的弱引用计数器对象的成员函数attemptIncStrong来试图增加该对象的强引用计数。如果能够成功增加对象的强引用计数,那么就可以成功地把一个弱指针升级为一个强指针了。参数refs是一个类型为weakref_type的指针,因此,接下来会调用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;
    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 , 
            // 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 and the 
            // implementation allows it to happed.
            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 benn incremented by
        // someone else, the implementor of onIncStrongAttempted() is holding
        // an unneeded reference. So call onLastStrongRef() here to remove it.
        // 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->addWeakRef(id);
    impl->addStrongRef(id);

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

    return true;
}

weakref_type类的成员函数attemptIncStrong试图增加目标对象的强引用计数,但是有可能会增加失败,因为目标对象可能已经被释放了,或者该目标对象不允许使用强指针引用它。
前面的内容源码分析中提到过,在增加对象的强引用计数时,同时也会增加该对象的弱引用计数。因此,attemptIncStrong函数实现中首先调用成员函数incWeak来增加对象的弱引用计数。如果后面增加对象的强引用计数失败,则会调用成员函数decWeak来减少对象的弱引用计数。前面提到,wp类的成员变量m_refs实际上指向的是一个weakref_impl对象,因此,weakref_impl* const impl = static_cast<weakref_impl*>(this);可以安全的执行。
一个弱指针所引用的对象可能处于两种状态。

  1. 第一种状态是该对象同时也正在被其他强指针所引用,即它的强引用计数值大于0,并且不等于INITIAL_STRONG_VALUE。
  2. 第二种状态是该对象没有被任何强指针引用,即它的强引用计数值小于等于0,或者等于INITIAL_STRONG_VALUE。

接下来,我们就根据这两种情况来分析一个弱指针是如何升级为一个强指针的。


对象同时被其他强指针引用

这种情况比较简单,因为这时候对象一定是存在的,因此,我们可以安全的将这个弱指针升级到强指针。

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;
}

上面的代码也正是处理第一种情况,通过android_atomic_cmpxchg方法对对象的强引用计数增加1。这里之所以采用android_atomic_cmpxchg方法而不采用android_atomic_inc,是因为android_atomic_cmpxchg是一个与CPU体系结构相关的函数,在某些提供了特殊指令的体系结构中,该方法执行原子性加1的效果更高。
函数android_atomic_cmpxchg的源码如下所示:

int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr);
#define android_atomic_cmpxchg android_atomic_release_cas;

可以看到,android_atomic_cmpxchg只是一个宏定义,它实际上指向的函数是android_atomic_release_cas。函数android_atomic_release_cas的工作原理是:如果它发现地址addr的内容等于参数oldvalue的值,那么它就会将地址addr的内容修改为newvalue,然后给调用者返回0,表示修改地址addr的内容成功。否则,就什么也不做,然后给调用者返回1。
回到函数attemptIncStrong调用android_atomic_cmpxchg的地方,经过android_atomic_cmpxchg(curCount, curCount + 1, &impl->mStrong)的调用,impl对象的强引用计数加1。为什么外面会有一层while循环(while (curCount > 0 && curCount != INITIAL_STRONG_VALUE))呢,是因为多线程并发时,可能其他线程先修改了对象的强引用计数值,导致android_atomic_cmpxchg不能成功的增加对象的强引用计数。如果出现这种情况,就需要重新获取当前对象的强引用计数值,然后再次调用android_atomic_cmpxchg函数来增加对象的强引用计数值。


对象没有被其它强指针引用

在这种情况下,判断语句if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE)的值为true,这时候情况就比较复杂了,因为此时对象可能存在,也可能不存在,要进一步判断。
如果对象的强引用计数值等于INITIAL_STRONG_VALUE,即if (curCount == INITIAL_STRONG_VALUE) {的值为true,那么就说明这个对象从来没有被强指针引用过。因此,接下来根据对象的生命周期控制方式或者对象的实现来判断是否允许将一个引用了它的弱指针升级为强指针。如果对象的生命周期只受强引用计数影响,那么就可以成功的将该弱指针升级为强指针。

(ps:这一点比较好理解,因为如果对象的生命周期只受强引用计数影响,而此刻对象又没有被强指针引用过,那么它就必然不会被释放)。

如果对象的生命周期受弱引用计数影响,即表达式impl->mFlags & OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK的值为false,那么就说明对象现在肯定是存在的,因为现在正有一个弱指针在引用它。但是,这种情况需要进一步调用对象的成员函数onIncStrongAttempted来确认对象是否允许强指针引用它。如果对象的成员函数onIncStrongAttempted的返回值为true,就说明允许使用强指针来引用它,因此,这种情况也可以成功将该弱指针升级为强指针。RefBase类的成员函数在参数flags为FIRST_INC_STRONG的情况下,是允许将一个指向只受弱引用计数影响生命周期的对象的弱指针升级为一个强指针的,它的源码实现如下:

bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id)
{
    return (flags & FIRST_INC_STRONG) ? true : false;
}

如果对象的强引用计数值小于等于0,说明对象之前被强指针引用过,因此,就必须要保证对象的生命周期要受到弱引用计数的影响,否则,对象就已经被释放掉了。当对象的生命周期控制标志值的OBJECT_LIFETIME_WEAK位不等于0时,就说明对象的生命周期受到弱引用计数的影响,但是这时候还不能直接将该弱指针升级为强指针,因为对象可能不希望它被强指针所引用。因此,就需要调用对象的成员函数onIncStrongAttempted来进一步确认。
如果最终的allow为false,那么就说明弱指针升级为强指针失败,函数首先调用decWeak(id);来减少对象的弱引用计数(ps:因为函数开始首先增加了弱引用计数,这里升级失败,需要再减回去),然后返回false给调用者。
如果最终的allow为true,那么就说明弱指针可以升级为强指针,因此接下来就会增加对象的强引用计数。


应用实例分析

我们在应用目录下建立一个c++应用程序weightpointer来说明强指针和弱指针的使用方法。应用程序weightpointer的实现很简单,它只有一个源文件weightpointer.cpp和一个编译脚本文件Android.mk。


weightpointer.cpp

weightpointer.cpp的源码如下:

#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("Test Weak Class: \n");
    WeakClass* pWeakClass = new WeakClass();
    testWeakClass(pWeakClass);

    return 0;
}

Android.mk

Android.mk内容如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := weightpointer
LOCAL_SRC_FILES := weightpointer.cpp
LOCAL_SHARED_LIBRARIES := \
    libcutils \
    libutils
include $(BUILD_EXECUTABLE)

结果分析

首先,在手机上运行weightpointer的结果如下:
weightpointer

weightpointer.cpp代码中,首先定义了一个继承自RefBase类的WeightClass类,它只有一个成员函数printRefCount,用了打印对象的引用计数,包括强引用计数和弱引用计数。然后又定义了两个类StrongClass和WeakClass,它们都继承自WeightClass。其中,StrongClass类对象的生命周期只受强引用计数影响,WeakClass类对象的生命周期同时受到强引用计数和弱引用计数的影响。接着,又定义了两个测试函数testStrongClass和testWeakClass,它们分别用了测试强指针和弱指针的使用场景,以验证我们对强指针和弱指针的实现原理分析。

在testStrongClass函数中,首先将一个StrongClass对象赋值给一个弱指针wpOut。因此,最先打印出来的该StrongClass对象的强、弱引用计数值应该分别是0和1。接下来,在将StrongClass对象赋值给一个强指针spInner,因此,接下来该StrongClass对象打印出来的强、弱指针计数分别为1和2。函数继续向下执行时,由于已经超出了强指针spInner的作用域,spInner指针的析构函数被调用,因此,此时该StrongClass对象的强、弱引用计数值为0和1。由于该StrongClass对象的生命周期只受强引用计数影响,当强引用计数为0时,该StrongClass对象被自动释放掉,因此打印出Destory StrongClass Object。函数最后试图将wpOut由弱指针升级为强指针,由于此时StrongClass已经被释放掉,wp的指向对象的地址值为NULL,因此会升级失败。

在testWeakClass函数中,首先将一个WeakClass对象赋值给一个弱指针wpOut,因此,首先打出来的该WeakClass对象的强、弱引用计数值为0和1。然后,再将WeakClass对象赋值给一个强指针spInner,因此,接下来打开的WeakClass对象的强、弱指针计数值为1和2。由于继续执行超过了强指针spInner的作用域,spInner的析构函数被调用,因此,此时该WeakClass对象的强引用计数值和弱引用计数值应该为0和1。由于此时该WeakClass对象的生命周期同时受强、弱引用计数影响,此时WeakClass对象不会被释放掉。函数接下来将wpOut升级为强指针成功,最后获得的强指针spOut所引用的对象地址就不为0了。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值