Android系统之智能指针sp和wp回顾

概述

Android中的智能指针是对C++中的对象回收机制的封装,C++中的构造函数和析构函数是在对象new出来和delete的时候调用的,但是一个在销毁一个对象的时候,我们需要手动的调用delete关键字来销毁,但是在Java中我们无需在乎这些对象的销毁工作,都是由垃圾回收器来做了,所以在Android系统层,为了达到Java的这个自动管理对象的效果,就出现了智能指针的概念了,他的出现类似于Java中的回收器

内部实现也很简单,就是用两个变量来控制,一个是强引用计数变量,一个是弱引用计数变量,这两个变量都是int类型的,表示一个对象被引用多少次,两个变量会依据具体的生命管理周期模式来决定是否释放对象。

android/system/core/libutils/RefBase.cpp
android/system/core/libutils/include/utils/RefBase.h
android/system/core/libutils/include/utils/StrongPointer.h

RefBase

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

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

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

智能指针的实现


如果一个类想使用智能指针,那么必须满足下面两个条件:

a. 该类是虚基类RefBase的子类或间接子类
b. 该类必须定义虚构造函数。如virtual ~MyClass();

实际对象的释放,可分为强引用控制和弱引用控制。所谓强引用控制,指的是强引用数mStrong为0时,释放实际对象;弱引用控制,则指的是弱引用数mWeak为0时,才释放实际对象。

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点:

第一点使用它需要继承LightRefBase(模板类)

public LightClass: public LightRefBase

第二点LightRefBase类只有一个成员变量mCount用来描述一个对象的引用计数值。

第三点需要知道轻量级指针的实现类和强指针的实现类是同一个类sp。

强指针

与轻量级指针不同,强指针不是直接使用一个整数来维护对象的引用计数的,而是使用一个weakref_impl对象,这个对象是继承RefBase类(一个类要使用强指针和弱指针必须继承RefBase)中的内部类weakref_type类,其中weakref_type仅仅只定义了引用计数维护接口,具体实现是weakref_type。(具体关系如下图,图是手码的,一个是继承关系,一个是引用关系)

RefBase的incStrong函数干了哪些事情呢?(主要有三步,第三步是第一次强引用的一些逻辑处理,这里不分析)

1.增加弱引用计数(这个看起来好像与函数的名字有点相互违背,这个后面会解释)

具体过程:通过mRefs的incWeak方法来增加对象的弱引用计数(可以配合类图理解),mRefs是Weakref_impI类型的,Weakref_impl又继承了inWeak方法,实际上调用的是weakref_type的方法

2.增加强引用计数。

通过android_atomic_inc函数增加强引用计数值(返回增加前的值,这里注意是之前)

可以看出强指针类增加对象的强引用计数的同时也会增加弱引用计数,即一个对象的弱引用计数一定是大于或者等于它的强引用计数的。(sp的构造函数就干了这么些事情)

那么sp的析构函数干了什么事情呢?(对应函数decStrong)

1.减少对象的强引用计数,当强引用计数为0时(实际上不是0,这里用0好解释),即不再被强指针引用时。此时需要判断标识位mFlags(上面提过)是否为1,如果不为1,就会释放对象所占的内存,同时也会导致RefBase类的析构函数调用。

2.减少对象的弱引用计数,一旦发现弱引用计数为0时,把引用计数对象mRefs(weakref_impl类型)也释放掉(前面提过,建议回头看看方便理解)。前面说过,一个对象的弱引用计数一定大于或者等于强引用计数的,当强引用计数为0时,会释放掉RefBase对象,但当此时弱引用计数大于0时,不能将mRefs也释放掉,因为还有其他的弱指针通过weakref_impl对象来引用实际的对象。

弱指针

弱指针同样从RefBase类继承下来,因为RefBase提供了弱引用计数器。弱指针类的实现类为wp。弱指针使用的是类型为weakref_type*的成员变量m_refs维护对象的弱引用计数。

弱指针和强指针有一个很大的区别,就是弱指针不可以直接操作它所引用的对象,因为它所引用的对象可能是不受弱引用计数控制的,即它所引用的对象可能是一个无效的对象。因此,如果需要操作一个弱指针所引用的对象,那么就需要将这个弱指针升级为强指针,这是通过它的成员函数promote来实现的。如果升级成功,就说明该弱指针所引用的对象还没有被销毁,可以正常使用。

下面着重介绍wp的promote函数。先来看两段源代码代码(纯手码截图,下次用markdown编辑器写,这么写太sb了)

RefBase.h
参数p指向对象的地址,而参数refs指向该对象内部的一个弱引用计数器对象。只有在对象地址不为null的情况下,才会调用它内部的弱引用计数器对象的成员函数attempIncStrong来试图增加该对象的强引用计数。如果能够成功增加对象的强引用计数,那么就可以成功地把一个弱指针升级为一个强指针。

attempIncStrong看着是不是很熟悉,可以从之前的图中找到。

这个成员函数试图增加目标对象的强引用计数,但是有可能会增加失败,因为目标对象可能已经被释放了,或者该目标对象不允许使用强指针引用它。

(attempIncStrong中有个有意思的逻辑)

之前提过增加对象强引用计数时,同时也会增加该对象的弱引用计数。

分割线(逻辑来了)
1.先调用成员函数incWeak来增加对象的弱引用计数 2.如果后面增加对象的强引用计数失败,则调用decWeak来减少对象的弱引用计数。
一个弱指针所引用的对象可能处于两种状态。(下面均摘自原文)

第一种:该对象同时也被其他强指针对象所引用,此时可以安全地将这个弱指针升级为强指针。

第二种:该对象没有被任何强指针引用。这里情况就比较复杂了。需要根据对象生命周期来判断

1.如果对象生命周期只受强引用计数影响,那么就可以成功将该弱指针升级为强指针。因为它受强引用计数影响,而此时该对象又没有被强指针引用过,那么它必然不会被释放。

2.如果只受弱引用计数影响,首先我们可以确定对象现在一定是存在的,因为现在有一个弱指针引用它。但是,这种情况需要进一步调用对象的成员函数onIncStrongAttempted来确认对象是否允许强指针引用它。如果返回为true说明允许则成功将该弱指针升级为强指针。如果返回为false,则说明升级失败。

RefBase构造和mRefs

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

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

weakref_impl从weakref_type派生,mRefs才是真正的“管家”。


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

智能指针的三种生命周期管理方式

1). 如果对象的标志位被设置为0,那么只要发现对象的强引用计数值为0,那就会自动delete掉这个对象;
2). 如果对象的标志位被设置为OBJECT_LIFETIME_WEAK,那么只有当对象的强引用计数和弱引用计数都为0的时候,才会自动delete掉这个对象;
3). 如果对象的标志位被设置为OBJECT_LIFETIME_FOREVER,那么对象就永远不会自动被delete掉,谁new出来的对象谁 来delete掉。

Android中的智能指针不是真正意义上的指针,他是sp和wp的类对象,用来管理对象的生命周期的中间类;Android系统层为了解决对象的自动管理就引入了智能指针机制,;可以看出强指针类增加对象的强引用计数的同时也会增加弱引用计数,即一个对象的弱引用计数一定是大于或者等于它的强引用计数的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

安德路

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

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

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

打赏作者

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

抵扣说明:

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

余额充值