Jimmy Chen

A Programmer

(原创)Android智能指针

智能指针的目标

  在使用指针的时候容易出现的问题不外乎下面几个。首先,指针在使用之前都必须初始化,这个还算容易解决,在创建指针变量的时候同步初始化就好了;第二个问题就是经常忘记delete,就我的经验来看,这个还是很容易忘记的,在一个大型程序中要是有那么几个地方忘记执行delete,长久来看系统内存肯定会被消耗完;第三个问题就是就算记得delete,但是也不是说delete就delete的,要是还有别的对象在引用这个对象,然后被delete了,那么在其他地方访问这个对象的时候程序肯定会奔溃的。

  有了智能指针后,上面的问题就好办了,首先创建智能指针的时候也同时创建需要管理的对象,然后将需要管理的对象委托给智能指针来管理就好了。在智能指针内部管理着一个引用计数值,当有新的智能指针引用这个对象时,引用计数值加一,当本来引用该对象的智能指针指向其他对象时,那么引用计数值就减一,当引用计数值减为0的时候就智能指针会帮忙将对象delete,也就解决了忘记delete以及在错误时刻进行delete的困境了。这样上面出现的问题就都能很好的解决了。

轻量级指针

  首先看一下轻量级指针,轻量级指针属于模板类,要想使用轻量级指针的功能只要在创建自身类的时候继承这个类就OK了,然后在这个类内部组织管理着一个引用计数值。这个值的功能和前面说的一样,起到控制对象生命周期的作用。incStrong和decStrong起到减少和增加引用计数值的功能。

  因为LightRefBase内没有用来保存待委托对象的指针,所以LightRefBase不算是智能指针,大概算是指针的升级版本。以这个类作为基类的类还需要搭配真正的智能指针才能发挥作用。之后会介绍真正的智能指针sp和wp。

强指针

sp类

sp类相对简单,大家看一下代码的实现基本也能了解,下面会配合Refbase进行简单的分析。

  强指针使用的引用计数类是RefBase,它比LightRefBase复杂得多,所以后者才会被称为轻量级指针。下面看一下RefBase的代码:

  RefBase和LightRefBase一样提供了incStrong和decStrong成员函数来操作引用计数器;而RefBase和LightRefbase类最大的区别就是它不像LightRefBase那么简单,只提供一个引用计数器,而是提供了一个强引用计数器和一个弱引用计数器。这两种计数器的功能是由weakref_impl类的变量mRefs提供的。

weakref_impl类

  RefBase类的成员变量mRefs的类型为weakref_impl指针,这个类的代码在RefBase.cpp文件内,里面的代码看似很复杂,其实细心了解下里面有一个DEBUG_REFS宏,这个宏里面的代码只有在Debug版本下才会去实现,否则为空,所以基本可以不用看。

Weakref_impl类是weakref_base的子类,这个是接口与实现分离的思想。

RefBase的incStrong函数

  这里的other就是实际的对象,这个对象可以是继承了LightRefBase的对象也可以是继承了RefBase的对象,因为这里主要分析RefBase对象,所以这里以及下面的内容都假设other对象是继承了RefBase的内容的,下面看RefBase的incStrong函数

其中mRefs实在RefBase的构造函数中创建的

重新回到incStrong函数中,我们会发现这个函数中其实只是做了三件事情:

  • 增加弱引用计数 refs->incWeak(id);
  • 增加强引用计数 const int32_t c = android_atomic_inc(&refs->mStrong);
  • 如果发现是第一次调用对象的incStrong,那么就会修正mStrong引用计数,然后调用这个对象的onFirstRef函数

  在调用weakref_impl的构造函数的时候会将mStrong的值初始化为INITIAL_STRONG_VALUE=1<<28;那么在执行加1操作后,mStrong就等于1<<28+1;返回的值c等于加1前的值,即1<<28;所以第一次调用incStrong后需要对mStrong的值进行修正,加上-INITIAL_STRONG_VALUE正好。

  现在回头看增加弱引用计数的代码,通过调用weakref_impl的incWeak来对弱引用计数进行加1操作,而weakref_impl类的incWeak则是直接从父类weakref_base中继承来的。

上面代码增加弱引用计数主要是执行android_atomic_inc方法来完成的。

RefBase的decStrong函数

上面的代码调用RefBase类的decStrong函数

  上面代码先将强引用计数减1,如果发现返回值为1的话,就代表调用此时强引用计数值已经为0了,那么就调用onLastStrongRef函数,这个函数在RefBase中的实现也是为空的,一般是留给子类来实现。然后判断mask是否为OBJECT_LISTTIME_STRONG,如果是,代表该对象的生命周期受强引用计数值控制,当这个对象的强引用计数值为0时,就将这个对象delete掉。

  对弱引用计数值的操作则是调用decWeak.

在这个函数中,当弱引用计数不为1则直接return,如果为1那么减1后就为0了,需要对对象进行delete操作。而弱引用计数为1又分为两种情况:


  第一种情况为对象的生命周期只受强引用控制,而当强引用计数为初始值的时候,就要删除impl->mBase就是删除实际的对象。而RefBase被删除的时候就会调用析构函数,而在析构函数中决定是否需要将mRefs删除。

当时如果强引用指数不为初始值的时候,就直接调用delete impl,那是因为在decStrong函数中就已经将实际的对象delete掉了。


  第二种情况,当对象的生命周期不受强引用控制时,先调用onLastWeakRef函数,然后如果对象的生命周期时由弱引用控制,就直接删除RefBase对象,当然在RefBase的析构函数中也会删除mRefs对象。

弱指针

wp类

  与强指针类相比,他们都有一个成员变量m_ptr指向目标对象,但是弱指针还有一个额外的成员变量m_refs,他的类型时weakref_type指针,下面我们分析弱指针的构造函数时再看看他是如何初始化的。这里我们需要关注的是构造函数和析构函数。

wp类的构造函数

先看看wp类的构造函数

  这里调用RefBase的createWeak函数,返回值为m_refs。

  在RefBase的createWeak函数中,直接调用weakref_impl的incWeak函数,这个函数之前分析过了就是增加弱引用计数的,同时返回mRefs给调用函数。

  接下分析析构函数,这里直接调用weakref_impl的decWeak函数,前面分析过这个函数了。在这个函数里面弱引用次数减1,然后决定是否需要delete impl等。

升级为强指针

  分析到这里,弱指针还没有分析完成,这里面还有一个非常重要的特性还没有分析,那就是将弱指针升级为强指针的操作,是直接调用其中的promote函数

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注