现在的位置: 首页 > 综合 > 正文

Android sp,wp,RefBase浅析

2018年02月10日 ⁄ 综合 ⁄ 共 1637字 ⁄ 字号 评论关闭

对于native层new出来的c++对象的生命周期的管理,Android通过对这个对象引用计数的方式实现对象生命周期的管理(自动析构和释放内存)。为此Android提供了两个引用计数帮助类:sp,wp。sp,wp都是模板类,模板参数就是继承了RefBase的子类。sp代表强引用,wp代表弱引用。根据生命周期的管理策略(比如对象的周期受强引用计数管理,或者受强引用和弱引用共同管理)的不同,这两种引用对对象生命周期的影响有所不同。

RefBase的类图如下:

RefBase中保存着一个指向weakref_impl类的指针。通过weakref_impl的成员变量保存着RefBase类的引用计数。mStrong表示强引用计数的个数,mWeak表示弱引用计数的个数。假设有如下的代码:

class A:public RefBase {
public:
    A() {
        cout<<"constructor A"<<endl;
    }
    
    ~A() {
        cout<<"destructor A"<<endl;
    }
};

{
sp<A> pA1 = new A();
sp<A> pA2 = pA1; 
}

以上代码通过new的方式产生出来的A的对象的强引用计数和弱引用计数为2,即weakref_imp中mStrong和mWeak的值为2. 当new A()完成的时候,A的基类RefBase还new了一个weakref_iml对象,用来记录A的强弱引用计数。当把new A()所产生的A*指针作为参数构造一个sp<A>对象的时候,在sp<A>的copy constructor中会调用A的incStrong()方法,这个方法中先增加A的弱引用计数一次,然后增加强引用计数一次,强弱引用计数此时为1.当把pA1作为参数调用sp<A> 
的赋值函数产生出pA2的时候,在赋值函数中再次调用A的基类RefBase的incStrong函数,这样强弱引用计数就变为了2.

过了pA1的作用域之后,pA1的析构函数被调用,在析构函数中会调用A的decStrong函数减少一次强弱引用计数。同理,过了pA2的作用域之后,也会减少一次强弱引用计数,这时,强弱引用计数就变为了0.在RefBase类的decStrong函数中会做判断,当引用计数为1的时候,表明本次调用后,强引用计数会为0,如果对象的生命周期只受强引用计数管理,这个时候就会调用delete来释放自己的内存。

从上面的分析来看,如果对象的生命周期只受强引用计数管理(默认如此),当对象的强引用计数为0的时候就会delete掉这个对象,而不管弱引用计数的值,当弱引用计数为0的时候就会delete掉weakref_imp类。那么弱引用计数究竟有什么作用呢,当调用了RefBase的extendObjectLifetime方法,参数为OBJECT_LIFETIME_WEAK,这将改变对象的生命周期的管理策略为强弱引用计数同时决定何时delete对象。

一般而言,程序代码中最好只有一个地方是采取sp的方式保存着new出来对象的地址,其他的用处用wp。这样对象的生命周期只受一处管理,其他地方通过wp来使用A的时候,先通过promote()函数看所保存的对象是否已经被释放。如果没有被释放,就可以返回一个sp类供调用者使用。

在实际使用中要注意的是:时刻要清楚自己new出来的对象的引用计数的次数。清楚自己对sp,wp构造,赋值等操作对引用计数的影响。比如有以下代码:

{
sp<A> pA1 = new A();
pA1->decStrong();
}

那么在花括号的作用域之后,程序会崩溃。原因如下:

第一句,强引用和弱引用计数为1.

第二句,强引用计数为为0,弱引用计数为0,A的对象被delete;但是sp<A>中还是保存着A的指针;

过了作用域之后,pA1的析构函数被调用,这个时候会再次通过pA1中保存的A的指针调用decStrong函数。但是此时,A的对象已经被delete。

所以程序崩溃。

下面是sp,wp与RefBase的类图关系:

抱歉!评论已关闭.