Android4.4 引用计数小结

最近研究Android4.4源码,小结一下关于引用计数部分。跟书上讲的还有点不太一样,除了目录有变化外,代码有点变动,根据书中以及网上参考的一些资料,做个小的总结:

声明在system\core\include\utils目录下,实现在system\core\Libutils下。

为了在VS下编译测试,所以修改了一些东西:【不得不说模板调试还真是比较痛苦】

它的基类RefBase定义如下:

class RefBase
{
public:
            void            incStrong(const void* id) const;
            void            decStrong(const void* id) const;
            void            forceIncStrong(const void* id) const;
            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);
        bool                attemptIncWeak(const void* id);
        int32_t             getWeakCount() const;
        void                printRefs() const;
        void                trackMe(bool enable, bool retain);
    };
            weakref_type*   createWeak(const void* id) const;
            weakref_type*   getWeakRefs() const;
    inline  void            printRefs() const { getWeakRefs()->printRefs(); }
    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;
};

我到现在都不明白为什么要用内部类,有什么意思呢?搞不懂。

RefBase中有一个内部类,weakref_type,里边主要是关于incWeak,decWeak操作等函数,这里边只有函数, 没有其他变量,主要是被用来继承使用的。事实上weakref_impl类就是从weakref_type继承而来的,注意代码中最后几行中,weakref_impl的声明,class weakref_impl,这种写法也是内部类一种,只不过定义的时候需要加上类的全名,如  class RefBase::weakref_impl : public RefBase::weakref_type。根据测试结果,其实内部类的this和外部类的this指向的地址,简单来说,外部类的this指向类的头地址,而内部类的this而是根据内部类对象的位置偏移而设定,也就是说,内部类的this地址其实指向外部类的一部分,而且使用class weakref_impl这种声明方式,定义只能使用指针,而不能是变量,我的猜想是,因为这个类在RefBase中只是一个声明,不知道具体内容,所以没法确定大小,只能定义指针了。

   注意,默认构造函数是protected,复制构造函数被声明为private,也就是说,没法通过定义一个RefBase变量了,这样设计的目的就是被这个类只能被继承使用,即所谓的基类型。【忘了说了,RefBase中只有一个成员变量:weakref_impl* const mRefs】

   类中的那几个虚函数是调试版本所用,发行版没有实现,

   在内部类weakref_type中,第一个函数是:

 RefBase*            refBase() const;

返回值是RefBase的指针,它的实现如下:

	RefBase* RefBase::weakref_type::refBase() const
	{
		return static_cast<const weakref_impl*>(this)->mBase;
	}

先把weakref_type类型强制转换为weakref_impl类型,在类weakref_impl中有一个变量是mBase,其类型就是RefBase的。
反正我个人感觉是,如果自己实现的话,从基类指针强制向派生类转的话,那么一定要注意地址空间大小。这里为什么不会出错呢?
这里不得不中断来看weakref_impl的实现,它的声明与实现都在RefBase.cpp中:

class RefBase::weakref_impl : public RefBase::weakref_type
	{
	public:
		volatile int32_t    mStrong;
		volatile int32_t    mWeak;
		RefBase* const      mBase;//指向RefBase本身
		volatile int32_t    mFlags;

		weakref_impl(RefBase* base)
			: mStrong(INITIAL_STRONG_VALUE)
			, mWeak(0)
			, mBase(base)
			, mFlags(0)
		{
		}
	};

里边一些在调试模式下的函数我都删去了,没什么用,所以就剩下这简单的一点点东西了,多了4个变量,前两个分别是强弱引用计数,第三个就是它所在的外部类的引用,因为在C++中的内部类无法引用所在外部类的东西,个人觉得这个变量就是为解决这个问题的,然后是个标志,主要是被函数extendObjectLifetime来改变,看当前变量是被强引用控制还是被弱引用控制,还是不受智能指针的控制,这个暂时不必理会,在提升指针时会遇到。
   然后就是它的构造函数,根之前的版本不一样,删去了mStrongRefs这个变量,不过这个变量是在调试版中的,所以有没有一个样。回到RefBase类中,他的构造函数如下所示:

RefBase::RefBase()
		: mRefs(new weakref_impl(this))
	{
	}

这个跟之前的版本一样,所以当一个类型(当然这个类型必然是继承与RefBase类)被new出来之后,必然会new一个weakref_impl对象,这样我们就不用担心从weakref_type指针强制转换为weakref_impl类型时会产生问题了,这个真实的对象是有的,从头到尾一定记住了weakref_type类型仅仅是对函数的一个封装,其实我觉得用专门一个类就好了,不知道为什么一定要用内部类。
还有一个比较奇怪的地方就是,在基类RefBase中定了几个友元,其中一个是weakref_impl,其中一个原因是我觉得应该是内部类没有访问外部类的权限,所以为了访问,设置生了友元。
关于实现,举几个例子:

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);
	}
	void RefBase::incStrong(const void* id) const
	{
		weakref_impl* const refs = mRefs;
		refs->incWeak(id);
		const int32_t c = android_atomic_inc(&refs->mStrong);
		if (c != INITIAL_STRONG_VALUE)  {
			return;
		}
		android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
		refs->mBase->onFirstRef();//自己测试时这种函数直接删掉
	}

看着两个区别,weakref_type类的所有实现均是第一步必然是将自身强制转换为weakref_impl类型,然后所有操作其实都是通过impl类型来完成的,因为引用计数的增加是通过impl类中的mStrong和mWeak来实现的。
RefBase类的所有实现军事直接通过mRefs这个变量来操作的,它就是实际指向对象的指针。
忘了提醒一下,所有函数后缀是Ref都是调试所用,发行版没有实现,所以在真实调试的时候(我是直接抠出来放到vs中调试的,表示在linux下直接编译调试Android源码太过于麻烦了),可以直接删掉。总的函数如下图所示:

Android4.4 引用计数小结_第1张图片

在图中可以看到,RefBase中只负责强引用的工作,所以它只有incStrong,decStrong,forceIncStrong等函数操作,而弱引用则需要通过weakref_type类型来操作,因此,weakref_type类型也仅仅只有incWeak,decWeak等操作,有个例外是attemptIncWeak和attempIncStrong函数操作,因为需要由弱生强,所以从弱引用到强引用需要特殊判断,大致的步骤在promote函数中,意思是,一个对象可能由弱引用控制,那么当弱引用为0时(此时必然强引用为0),可以delete掉,如果是强引用控制,那么只要强引用为0,无论弱引用多少,都可以delete掉,那么在使用的时候就需要判断对象到底还存在否,如果是永久性的,那么他的生命周期不受此限制,所以在提升的时候需要根据这些情况作出判断,因此,就多了这个关于强引用的函数。

最后说明一点,所有的操作均是对impl这个对象来操作的,无论是通过mRefs引用还是从type强制转换过来。

关于WP和SP

wp和sp分别是弱引用和强引用的模板类

sp定义在StrongPoint.h文件中(在之前的版本中,它是定义在RefBase这个文件中的)

声明如下:

template <typename T>
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();

    // Assignment
    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);
    //! Special optimization for use by ProcessState (and nobody else).
    void force_set(T* other);
    // Reset
    void clear();

    // Accessors

    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      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;
    void set_pointer(T* ptr);
    T* m_ptr;
};

总的来说,一个成员变量,m_ptr,注意了RefBase是一个真实的类,而SP是一个模板类,也就是说最终使用的时候模板中的类一定是从RefBase类继承而来的。

拿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> template<typename U>
sp<T>::sp(U* other) : m_ptr(other)
{
    if (other) ((T*)other)->incStrong(this);
}
	
template<typename T> template<typename U>
sp<T>::sp(const sp<U>& other)
: m_ptr(other.m_ptr)
  {
    if (m_ptr) m_ptr->incStrong(this);
  }

除了一个默认构造函数外,赋值和复制构造函数均是分两大类:同类型和异类行。

同类型是指模板类型为T,赋值过来参数也是T类型,异类行是指模板类型为T,而传递参数类型为U。

其所有的操作均是分为这两种类型来定义实现的,而WP的类中也是分3种情况的,因为除了这两种情况外,还需要从SP传递过来。一个对象一定是个强引用,但是其可以改为弱引用,这句是WP比SP多一种类型的原因。

2种类型的赋值与拷贝从赋值构造函数也可以看出,具体跟复制构造函数一个样。有一个很奇怪的问题是,这几个类中居然没有对*和->的重载操作。不明白为什么。

SP中的一个操作如下:

template<typename T>
sp<T>& sp<T>::operator = (const sp<T>& other) {
    T* otherPtr(other.m_ptr);
    if (otherPtr) otherPtr->incStrong(this);
    if (m_ptr) m_ptr->decStrong(this);
    m_ptr = otherPtr;
    return *this;
}

     一开始我也没有搞明白为啥这样,看了一会才明白,非常类似与STL中的SmartPoint,把other赋过来,首先将other本身的强引用增加,然后将本身的强应用减去,因为“权力”变了,减完之后,赋值。

WP就不再贴了,帖子长了自己都懒得看了,不过基本内容差不多,成员变量多了一个m_refs,这个指针主要操作弱引用部分。然后一个比较长的函数是promote函数,根据上边提到的不同情况,看是否可以将弱引用转为强引用。

其实一开始就没太搞明白这几层的关系,突然感觉C++都白学了,看了点例子,然后把这几个文件抠出来(这些与底层无关,把一些原子操作自己改掉了)用VS编译,测试,然后才有点小明白,如果直接在Android里编的话,当然我没试过,不过看书上讲可以调试框架,没尝试,先搞明白这底层的这点东西吧。


插一句,后来测试的时候发现一个类(只有函数,没有变量)中只要有虚函数,那么这个类默认大小为4个字节,如果没有,默认为一个字节



参考的文章:http://www.blogjava.net/mixer-a/archive/2012/04/17/374985.html


你可能感兴趣的:(android引用计数)