智能指针简单分析


       前一阵子好忙,忙的根本停不下来。现在还好点,能闲一些。所以抽空把之前没研究完的Android智能指针继续研究。

基础

简介

       写C++程序时对指针的使用一定要十分谨慎,自己new出来的对象,如果忘记了delete,多次之后就会有比较严重的内存泄露;如果对指针忘记初始化,有时会出现野指针问题;已经释放了申请的内存,但是还在使用指向它的指针,又是一个悬垂指针……其实还有很多使用不当的情况,但是C++又不是java,jvm拥有强大的垃圾回收机制。但是为了避免种种指针使用不当引发的问题,STL标准模板库提供了智能指针这个工具。
       Google在Android源码中也引入了智能指针这个概念,参考了java垃圾收集器和STL的智能指针,从而自己封装了一套工具类。

设计理念

       如果要合理管理内存回收,一般的做法就是使用引用计数的方法。每当有一个指针指向了一个new出来的对象时,就对这个对象的引用计数增加1,每当有一个指针不再使用这个对象时,就对这个对象的引用计数减少1,每次减1之后,如果发现引用计数值为0时,那么,就要delete这个对象了,这样就避免了忘记delete对象或者这个对象被delete之后其它地方还在使用的问题了。但是该如何实现这个引用计数呢?我们知道,C++中对象的创建和销毁会分别调用构造函数和析构函数,所以引用计数的工作就落实在他们身上,同时还应该重载一些运算符,重载一些拷贝构造函数,以完善智能指针的功能。

       在计算机科学领域中,提供垃圾收集(Garbage Collection)功能的系统框架,即提供对象托管功能的系统框架,例如Java应用程序框架,也是采用上述的引用计数技术方案来实现的,然而,简单的引用计数技术不能处理系统中对象间循环引用的情况。考虑这样的一个场景,系统中有两个对象A和B,在对象A的内部引用了对象B,而在对象B的内部也引用了对象A。当两个对象A和B都不再使用时,垃圾收集系统会发现无法回收这两个对象的所占据的内存的,因为系统一次只能收集一个对象,而无论系统决定要收回对象A还是要收回对象B时,都会发现这个对象被其它的对象所引用,因而就都回收不了,这样就造成了内存泄漏。这样,就要采取另外的一种引用计数技术了,即对象的引用计数同时存在强引用和弱引用两种计数,例如,Apple公司提出的Cocoa框架,当父对象要引用子对象时,就对子对象使用强引用计数技术,而当子对象要引用父对象时,就对父对象使用弱引用计数技术,而当垃圾收集系统执行对象回收工作时,只要发现对象的强引用计数为0,而不管它的弱引用计数是否为0,都可以回收这个对象,但是,如果我们只对一个对象持有弱引用计数,当我们要使用这个对象时,就不直接使用了,必须要把这个弱引用升级成为强引用时,才能使用这个对象,在转换的过程中,如果对象已经不存在,那么转换就失败了,这时候就说明这个对象已经被销毁了,不能再使用了

智能指针

       Android系统提供了强大的智能指针技术供我们使用,这些智能指针实现方案既包括简单的引用计数技术,也包括了复杂的引用计数技术,即对象既有强引用计数,也有弱引用计数。我们最常见的智能指针就是强指针sp(Strong Pointer)和弱指针wp(Weak Pointer)。要实现内存的自动释放,sp、wp必须结合RefBase这个类来使用,在Android中,大多数类的最上层基类都是RefBase类。我们就只看这两种比较重要的吧。

强指针

       强指针使用的引用计数类为RefBase,我们看看它的定义,位于system/core/include/utils/RefBash.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

class RefBase
{
public:
void incStrong(const void* id) const;//增加强引用计数
void decStrong(const void* id) const;//减少强引用计数

void forceIncStrong(const void* id) const;//强制增加强引用计数

......

class weakref_type //实现引用计数功能的接口类
{
public:
RefBase* refBase() const;

void incWeak(const void* id);//增加弱引用计数
void decWeak(const void* id);//减少弱引用计数

// acquires a strong reference if there is already one.
bool attemptIncStrong(const void* id);

// acquires a weak reference if there is already one.
// This is not always safe. see ProcessState.cpp and BpBinder.cpp
// for proper use.
bool attemptIncWeak(const void* id);

......
};

weakref_type* createWeak(const void* id) const;

weakref_type* getWeakRefs() const;
......

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 weakref_type; //将辅助类设为友元,方便操作完成计数功能
class weakref_impl;

RefBase(const RefBase& o); //重载拷贝构造函数,避免默认拷贝构造函数的浅拷贝现象,因此实现类里要实现深拷贝
RefBase& operator=(const RefBase& o);//赋值运算符重载

weakref_impl* const mRefs;//通过这个类实现引用计数
};

       RefBase类提供了incStrong和decStrong成员函数来操作它的引用计数器,复杂的引用计数技术同时支持强引用计数和弱引用计数,在RefBase类中,这两种计数功能是通过其成员变量mRefs来提供的。
       RefBase类的成员变量mRefs的类型为weakref_impl指针,从weakref_impl的类名来看,它应该是一个实现类,那么,就必然有一个对应的接口类,这个对应的接口类的就是RefBase类内部定义的weakref_type类了,这是一种把类的实现与接口定义分离的设计方法,也是一种多态的展现。weakref_impl的实现位于system/core/libutils/RefBash.cpp中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148


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 //如果是Release版本,

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*/) { }

#else //那就只有这么多,下面都是Debug版本的

weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
, mStrongRefs(NULL)
, mWeakRefs(NULL)
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
, mRetain(false)
{
}

~weakref_impl()
{
bool dumpStack = false;
if (!mRetain && mStrongRefs != NULL) {
dumpStack = true;
ref_entry* refs = mStrongRefs;
while (refs) {
refs = refs->next;
}
}

if (!mRetain && mWeakRefs != NULL) {
dumpStack = true;
ref_entry* refs = mWeakRefs;
while (refs) {

refs = refs->next;
}
}
}

void addStrongRef(const void* id) {

addRef(&mStrongRefs, id, mStrong);
}

void removeStrongRef(const void* id) {
if (!mRetain) {
removeRef(&mStrongRefs, id);
} else {
addRef(&mStrongRefs, id, -mStrong);
}
}

void addWeakRef(const void* id) {
addRef(&mWeakRefs, id, mWeak);
}

void removeWeakRef(const void* id) {
if (!mRetain) {
removeRef(&mWeakRefs, id);
} else {
addRef(&mWeakRefs, id, -mWeak);
}
}


private:
struct ref_entry
{
ref_entry* next;
const void* id;
int32_t ref;
};

void addRef(ref_entry** refs, const void* id, int32_t mRef)
{

if (mTrackEnabled) {
AutoMutex _l(mMutex);

ref_entry* ref = new ref_entry;
// Reference count at the time of the snapshot, but before the
// update. Positive value means we increment, negative--we
// decrement the reference count.
ref->ref = mRef;
ref->id = id;

ref->next = *refs;
*refs = ref;
}
}

void removeRef(ref_entry** refs, const void* id)
{

if (mTrackEnabled) {
AutoMutex _l(mMutex);

ref_entry* const head = *refs;
ref_entry* ref = head;
while (ref != NULL) {
if (ref->id == id) {
*refs = ref->next;
delete ref;
return;
}
refs = &ref->next;
ref = *refs;
}

ref = head;
while (ref) {
ref = ref->next;
}

}
}


mutable Mutex mMutex;
ref_entry* mStrongRefs;
ref_entry* mWeakRefs;

bool mTrackEnabled;
// Collect stack traces on addref and removeref, instead of deleting the stack references
// on removeref that match the address ones.
bool mRetain;

#endif//Debug版本结束
};

       这个类看起来很复杂,其实分为两部分看,还是很简单的。我们可以看到将它一分为二的条件宏:

1
2
3
4
5
#if !DEBUG_REFS  
...Release版本...
#else
...Debug版本...
#endif

       if宏里面的都是Release版本的,它的函数都是空实现;else宏下面的是Debug版本代码,它的成员函数都是有实现的,实现这些函数的目的都是为了方便开发人员调试引用计数用的,除此之外,还在内部实现了一个结构体ref_entry:

1
2
3
4
5
6
struct ref_entry
{
ref_entry* next;
const void* id;
int32_t ref;
};

       这个结构体也是为了方便调试的,这是一个链表的节点,那么我猜测Debug版本的引用计数调试时应该用的链表实现的。本来Debug版本我们可以不用关注,但这里本着学习C++的态度还是稍微看一看。比如我们先挑一个addRef(ref_entry* refs, const void id, int32_t mRef)函数看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void addRef(ref_entry** refs, const void* id, int32_t mRef)//要改变一个指针,必须用到二级指针
{

if (mTrackEnabled) {//ture
AutoMutex _l(mMutex);

ref_entry* ref = new ref_entry; //新建一个节点
// Reference count at the time of the snapshot, but before the
// update. Positive value means we increment, negative--we
// decrement the reference count.
//填充节点数据
ref->ref = mRef;
ref->id = id;
//头插法插入
ref->next = *refs;//将新节点的next指针指向头指针的地址
*refs = ref; //将头指针重新指向新节点
}
}

       这个应该很随意吧,就是头插法插入新节点。先把需要插入的节点的next域直线目标链表的头节点内容,然后把头结点重新指向新节点即可。接着我们再看看removeRef(ref_entry* refs, const void id)函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void removeRef(ref_entry** refs, const void* id)///要改变一个指针,必须用到二级指针
{

if (mTrackEnabled) {//true
AutoMutex _l(mMutex);

ref_entry* const head = *refs;//拿到指向目标指针的地址的指针
ref_entry* ref = head;//拿到这个指针的头结点
while (ref != NULL) {//从头往后遍历
if (ref->id == id) {//如果发现指定节点
*refs = ref->next;//则跳过这个节点的指向,直接指向下一个节点
delete ref;//然后删除指定节点
return;
}
//如果没有找到指定项,则继续向后遍历
refs = &ref->next;
ref = *refs;
}

ref = head;
while (ref) {
ref = ref->next;
}

}
}

       这一段应该也不难,就是从头到尾依次遍历每个链表节点,发现了指定项就把指向它的节点跳过指向它,直接指向它的下一个节点,然后删除指定项ok。

       总的来说,weakref_impl类只要提供了以下四个成员变量来维护对象的引用计数:

1
2
3
4
volatile int32_t    mStrong;
volatile int32_t mWeak;
RefBase* const mBase;
volatile int32_t mFlags;

       其中mStrong和mWeak分别表示对象的强引用计数和弱引用计数;RefBase类包含了一个weakref_impl类指针mRefs,而这里的 weakref_impl类也有一个成员变量mBase来指向它的宿主类RefBase;mFlags是一个标志位,它指示了维护对象引用计数所使用的策略,它的取值为0,或者以下的枚举值:

1
2
3
4
5
6
//! Flags for extendObjectLifetime()
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};

       引用计数器的类RefBase我们就暂时介绍到这里,现在先来看看强指针类和弱指针类的定义。强指针就是sp类,位于system/core/include/utils/StrongPointer.h下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

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

       上面是对sp类的一些定义,对于实现我们一个个来看。先看看拷贝构造函数:

1
2
3
4
5
6
template<typename T>  
sp<T>::sp(T* other)
: m_ptr(other)
{
if (other) other->incStrong(this);
}

       这里传进来的参数other一定是继承于RefBase类的,因此,在函数的内部,它调用的是RefBase类的incStrong函数,它定义在system/core/libutils/RefBash.cpp文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id);//增加弱引用计数

refs->addStrongRef(id);//这玩意儿在Release版本是空实现,上面分析过
const int32_t c = android_atomic_inc(&refs->mStrong);//增加强引用计数

if (c != INITIAL_STRONG_VALUE) {//如果不是第一调用则返回
return;
}
//如果是第一次调用,则要减去mStrong的初始化值
android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
refs->mBase->onFirstRef();//第一次调用的回调方法,可自己重写
}

       这个IncStrong函数主要做了三件事:1.增加若引用计数;2.增加强引用计数;3.如果发现是首次调用这个对象的incStrong函数,就会调用一个这个对象的onFirstRef函数,让对象有机会在对象被首次引用时做一些处理逻辑。
       android_atomic_inc函数相当于i++表达式,会对变量加一,但是返回的是加一前的值。这里的c返回的是refs->mStrong加1前的值,如果发现等于INITIAL_STRONG_VALUE,就说明这个对象的强引用计数是第一次被增加,因此,refs->mStrong就是初始化为INITIAL_STRONG_VALUE的,它的值为:

1
#define INITIAL_STRONG_VALUE (1<<28)

       因为首次调用初始值为INITIAL_STRONG_VALUE,所以为了以后能够正确计数,因此首次需要减去它的初始值,所以才有了—INITIAL_STRONG_VALUE操作,然后android_atomic_inc函数会自动加一,所以首次调用后引用计数为1,逻辑正确。

       然后我们继续回到incStrong函数中,看看弱引用计数是如何增加的。首先是调用weakref_impl类的addWeakRef函数,我们知道,在Release版本中,这个函数也不做,而在Debug版本中,这个函数增加了一个ref_entry对象到了weakref_impl对象的mWeakRefs列表中,表示此weakref_impl对象的弱引用计数被增加了一次。接着又调用了weakref_impl类的incWeak函数,真正增加弱引用计数值就是在这个函数实现的了,weakref_impl类的incWeak函数继承于其父类weakref_type的incWeak函数:

1
2
3
4
5
6
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);//强制类型转换
impl->addWeakRef(id);//及时是Debug版本,为啥这儿又多调了一遍这个调试方法?excuse me?
const int32_t c __unused = android_atomic_inc(&impl->mWeak);//增加弱引用计数
}

       可以看到最后一行增加了弱引用计数。不过第二行那个再次增加一次Debug版本的调试链表节点很诡异唉,不知为何。有人问过Android源码Binder通信机制模块的作者Dianne Hackborn ,他是这样回答的:

Ah I see. Well the debug code may be broken, though I wouldn’t leap to that
conclusion without actually testing it; I know it has been used in the
past. Anyway, these things get compiled out in non-debug builds, so there
is no reason to change them unless you are actually trying to use this debug
code and it isn’t working and need to do this to fix it.

       然而大神也不知道,那我们也不用管了= 。=

       所以sp在其构造函数里面所做的事情就是分别为目标对象的强引用计数和弱引和计数增加了1。

       然后我们再看看析构函数:

1
2
3
4
5
template<typename T>  
sp<T>::~sp()
{
if (m_ptr) m_ptr->decStrong(this);
}

       同样,这里的m_ptr指向的目标对象一定是继承了RefBase类的,因此,这里调用的是RefBase类的decStrong函数,这也是定义在system/core/libutils/RefBash.cpp文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id);//Debug版本的,没什么卵用
const int32_t c = android_atomic_dec(&refs->mStrong);//减少强引用计数

if (c == 1) {//如果强引用计数减为0了
refs->mBase->onLastStrongRef(id);//最后以一次引用了的回调
if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {//如果是强引用主导生命周期
delete this;//则直接回收这个对象
}
}
refs->decWeak(id); //减少弱引用计数
}

       decStrong函数每次先减少强引用计数;然后判断是不是强引用计数是否为0,如果为0则看看标志位是不是设了OBJECT_LIFETIME_WEAK或者其他的,如果没有设置,则直接delete对象;如果受OBJECT_LIFETIME_WEAK等标志位影响,比如受弱应用计数主导生命周期,则等到弱引用计数也为0才能delete对象;最后在减少弱引用计数。
       真正实现强引用计数减1的操作下面的refs->decWeak函数,weakref_impl类没有实现自己的decWeak函数,它继承了weakref_type类的decWeak函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);//强制类型转换
impl->removeWeakRef(id);//这个和上面那个为毛一个道理,Google大神都不知道,不用鸟
const int32_t c = android_atomic_dec(&impl->mWeak);//减少弱引用计数
if (c != 1) return;//如果没有减少到0,则直接返回
//如果弱引用计数减到0了
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {//如果生命周期是强引用主导,则直接删除对象
// This is the regular lifetime case. The object is destroyed
// when the last strong reference goes away. Since weakref_impl
// outlive the object, it is not destroyed in the dtor, and
// we'll have to do it here.
if (impl->mStrong == INITIAL_STRONG_VALUE) {
// Special case: we never had a strong reference, so we need to
// destroy the object now.
delete impl->mBase;
} else {
// ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl;
}
} else {//如果生命周期受弱引用或者FOREVER类型主导
// less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER}
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {//如果生命周期受弱引用主导
// this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
// is gone, we can destroy the object.
delete impl->mBase;
}
//如果是FOREVER,则需要手动删除
}
}

       这里减少了弱引用计数, 减1前如果发现不等于1,那么就什么也不用做就返回了,如果发现等于1,就说明当前对象的弱引用计数值为0了,这时候,就要看看是否要delete这个对象了:
       1. 如果目标对象的如果生命周期是强引用主导,就执行下面语句:

1
2
3
4
5
6
7
8
9
10
11
12
// This is the regular lifetime case. The object is destroyed
// when the last strong reference goes away. Since weakref_impl
// outlive the object, it is not destroyed in the dtor, and
// we'll have to do it here.
if (impl->mStrong == INITIAL_STRONG_VALUE) {
// Special case: we never had a strong reference, so we need to
// destroy the object now.
delete impl->mBase;
} else {
// ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl;
}

       这一段的注释很重要,通过看注释,这段意思是:这里是减少对象的弱引用计数的地方,如果调用到这里,那么就说明前面一定有增加过此对象的弱引用计数,而增加对象的弱引用计数有两种场景的,一种场景是增加对象的强引用计数的时候,会同时增加对象的弱引用计数,另一种场景是当我们使用一个弱指针来指向对象时,在弱指针对象的构造函数里面,也会增加对象的弱引用计数,不过这时候,就只是增加对象的弱引用计数了,并没有同时增加对象的强引用计数。因此,这里在减少对象的弱引用计数时,就要分两种情况来考虑。

       如果是前一种场景,这里的impl->mStrong就必然等于0,而不会等于INITIAL_STRONG_VALUE值,因此,这里就不需要delete目标对象了(impl->mBase),因为前面的RefBase::decStrong函数会负责delete这个对象。这里唯一需要做的就是把weakref_impl对象delete掉,但是,为什么要在这里delete这个weakref_impl对象呢?这里的weakref_impl对象是在RefBase的构造函数里面new出来的,理论上说应该在在RefBase的析构函数里delete掉这个weakref_impl对象的。在RefBase的析构函数里面,的确是会做这件事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
RefBase::~RefBase()
{
if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
// we never acquired a strong (and/or weak) reference on this object.
delete mRefs;
} else {
// life-time of this object is extended to WEAK or FOREVER, in
// which case weakref_impl doesn't out-live the object and we
// can free it now.
if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
// It's possible that the weak count is not 0 if the object
// re-acquired a weak reference in its destructor
if (mRefs->mWeak == 0) {
delete mRefs;
}
}
}
// for debugging purposes, clear this.
const_cast<weakref_impl*&>(mRefs) = NULL;
}

       但是不要忘记,在这个场景下,目标对象是前面的RefBase::decStrong函数delete掉的,这时候目标对象就会被析构,但是它的弱引用计数值尚未执行减1操作,因此,这里的mRefs->mWeak == 0条件就不成立,于是就不会delete这个weakref_impl对象,因此,就延迟到执行这里decWeak函数时再执行。

       如果是后一种情景,这里的impl->mStrong值就等于INITIAL_STRONG_VALUE了,这时候由于没有地方会负责delete目标对象,因此,就需要把目标对象(imp->mBase)delete掉了,否则就会造成内存泄漏。在delete这个目标对象的时候,就会执行RefBase类的析构函数,这时候目标对象的弱引用计数等于0,于是,就会把weakref_impl对象也一起delete掉了。

       2. 然后回到else逻辑中,如果目标对象的生命周期是受弱引用计数或者OBJECT_LIFETIME_FOREVER控制的:

1
2
3
4
5
6
7
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {//如果生命周期受弱引用主导
// this is the OBJECT_LIFETIME_WEAK case. The last weak-reference
// is gone, we can destroy the object.
delete impl->mBase;
}
//如果是FOREVER,则需要手动删除

       理论上说,如果目标对象的生命周期是受弱引用计数控制的,那么当强引用计数和弱引用计数都为0的时候,这时候就应该delete目标对象了,但是这里还有另外一层控制,我们可以设置目标对象的标志值为OBJECT_LIFETIME_FOREVER,即目标对象的生命周期完全不受强引用计数和弱引用计数控制,在这种情况下,即使目标对象的强引用计数和弱引用计数都同时为0,这里也不能delete这个目标对象,那么,由谁来delete掉呢?当然是谁new出来的,就谁来delete掉了,这时候智能指针就完全退化为普通指针了,这里的智能指针设计的非常强大。

       强指针这里就分析完了,小结一下:

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

弱指针

       弱指针所使用的引用计数类与强指针一样,都是RefBase类。我们直接看弱指针的实现,即wp类,位于system/core/include/utils/RefBase.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

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

// Operators

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<typename 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 { 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); }
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> friend class sp;
template<typename Y> friend class wp;

T* m_ptr;
weakref_type* m_refs;
};

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

       先看拷贝构造函数:

1
2
3
4
5
6
template<typename T>  
wp<T>::wp(T* other)
: m_ptr(other)
{
if (other) m_refs = other->createWeak(this);
}

       这里的参数other一定是继承了RefBase类,因此,这里调用了RefBase类的createWeak函数,它定义在system/core/libutils/RefBase.cpp文件中:

1
2
3
4
5
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}

       这里的成员变量mRefs的类型为weakref_impl指针,weakref_impl类的incWeak函数我们在前面已经看过了,它的作用就是增加对象的弱引用计数。函数最后返回mRefs,于是,弱指针对象的成员变量m_refs就指向目标对象的weakref_impl对象了。

       再看看析构函数:

1
2
3
4
5
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this);
}

       这里,弱指针在析构的时候,与强指针析构不一样,它直接就调用目标对象的weakref_impl对象的decWeak函数来减少弱引用计数了,当弱引用计数为0的时候,就会根据在目标对象的标志位(0、OBJECT_LIFETIME_WEAK或者OBJECT_LIFETIME_FOREVER)来决定是否要delete目标对象,前面我们已经介绍过了,这里就不再介绍了。

       分析到这里,弱指针还没介绍完,它最重要的特性我们还没有分析到。前面我们说过,弱指针的最大特点是它不能直接操作目标对象,这是怎么样做到的呢?秘密就在于弱指针类没有重载*和->操作符号,而强指针重载了这两个操作符号。但是,如果我们要操作目标对象,应该怎么办呢,这就要把弱指针升级为强指针了:

1
2
3
4
5
6
7
8
9
template<typename T>
sp<T> wp<T>::promote() const
{
sp<T> result;
if (m_ptr && m_refs->attemptIncStrong(&result)) {
result.set_pointer(m_ptr);
}
return result;
}

       升级的方式就使用成员变量m_ptr和m_refs来构造一个强指针sp,这里的m_ptr为指目标对象的一个指针,而m_refs则是指向目标对象里面的weakref_impl对象。主要就是初始化指向目标对象的成员变量m_ptr了,如果目标对象还存在,这个m_ptr就指向目标对象,如果目标对象已经不存在,m_ptr就为NULL,升级成功与否就要看refs->attemptIncStrong函数的返回结果了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

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) {//如果强引用计数大于0,且不是第一次引用,直接强引用计数加一
// we're in the easy/common case of promoting a weak-reference
// from an existing strong reference.
if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation.
curCount = impl->mStrong;
}

if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {//强引用计数小于等于0,或者从未被强指针引用过
// we're now in the harder case of either:
// - there never was a strong reference on us
// - or, all strong references have been released
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {//如果生命周期是强引用计数导向
// this object has a "normal" life-time, i.e.: it gets destroyed
// when the last strong reference goes away
if (curCount <= 0) {//强引用计数为0,说明对象已被销毁,则升级失败
// the last strong-reference got released, the object cannot
// be revived.
decWeak(id);
return false;
}

// here, curCount == INITIAL_STRONG_VALUE, which means
// there never was a strong-reference, so we can try to
// promote this object; we need to do that atomically.
while (curCount > 0) {//从未被强指针引用过,则强引用计数+1
if (android_atomic_cmpxchg(curCount, curCount + 1,
&impl->mStrong) == 0) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation (e.g.: another thread has inc/decStrong'ed us)
curCount = impl->mStrong;
}

if (curCount <= 0) {如果多线程操作,入另一个线程销毁了这个对象,则升级失败
// promote() failed, some other thread destroyed us in the
// meantime (i.e.: strong count reached zero).
decWeak(id);
return false;
}
} else {//如果生命周期收弱引用计数导向,或者是BJECT_LIFETIME_FOREVER
// this object has an "extended" life-time, i.e.: it can be
// revived from a weak-reference only.
// Ask the object's implementation if it agrees to be revived
if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {//尝试升级,默认为true
// it didn't so give-up.
decWeak(id);
return false;
}
// grab a strong-reference, which is always safe due to the
// extended life-time.
curCount = android_atomic_inc(&impl->mStrong);//升级后强引用计数+1
}

// If the strong reference count has already been incremented by
// someone else, the implementor of onIncStrongAttempted() is holding
// an unneeded reference. So call onLastStrongRef() here to remove it.
// (No, this is not pretty.) 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->addStrongRef(id);//没什么卵用

// now we need to fix-up the count if it was INITIAL_STRONG_VALUE
// this must be done safely, i.e.: handle the case where several threads
// were here in attemptIncStrong().
curCount = impl->mStrong;
while (curCount >= INITIAL_STRONG_VALUE) {//如果此弱指针是允计提升为强指针的,并且此目标对象是第一次被强指针引用,还需要调整一下目标对象的强引用计数值

if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
&impl->mStrong) == 0) {
break;
}
// the strong-count changed on us, we need to re-assert the situation,
// for e.g.: it's possible the fix-up happened in another thread.
curCount = impl->mStrong;
}

return true;
}

       这个函数的作用是试图增加目标对象的强引用计数,但是有可能会失败,失败的原因可能是因为目标对象已经被delete掉了,或者是其它的原因,下面会分析到。前面我们在讨论强指针的时候说到,增加目标对象的强引用计数的同时,也会增加目标对象的弱引用计数,因此,函数在开始的地方首先就是调用incWeak函数来先增加目标对象的引用计数,如果后面试图增加目标对象的强引用计数失败时,会调用decWeak函数来回滚前面的incWeak操作。

       这里试图增加目标对象的强引用计数时,分两种情况讨论,一种情况是此时目标对象正在被其它强指针引用,即它的强引用计数大于0,并且不等于INITIAL_STRONG_VALUE,另一种情况是此时目标对象没有被任何强指针引用,即它的强引用计数小于等于0,或者等于INITIAL_STRONG_VALUE。

        第一种情况比较简单,因为这时候说明目标对象一定存在,因此,是可以将这个弱指针提升为强指针的,在这种情况下,只要简单地增加目标对象的强引用计数值就行了:

1
2
3
4
5
6
7
8
9
10
while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {//如果强引用计数大于0,且不是第一次引用,直接强引用计数加一
// we're in the easy/common case of promoting a weak-reference
// from an existing strong reference.
if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation.
curCount = impl->mStrong;
}

       当我们在这里对目标对象的强引用计数执行加1操作时,要保证原子性,因为其它地方也有可能正在对这个目标对象的强引用计数执行加1的操作,前面我们一般是调用android_atomic_inc函数来完成,但是这里是通过调用android_atomic_cmpxchg函数来完成,android_atomic_cmpxchg函数是体系结构相关的函数,在提供了一些特殊的指令的体系结构上,调用android_atomic_cmpxchg函数来执行加1操作的效率会比调用android_atomic_inc函数更高一些。函数android_atomic_cmpxchg是在system/core/include/cutils/atomic.h文件中定义的一个宏:

1
2
3
4
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_release_cas,这个函数的工作原理大概是这样的:如果它发现addr == oldvalue,就会执行addr = newvalue的操作,然后返回0,否则什么也不做,返回1。在我们讨论的这个场景中,oldvalue等于curCount,而newvalue等于curCount + 1,于是,在addr == oldvalue的条件下,就相当于是对目标对象的强引用计数值增加了1。什么情况下addr != oldvalue呢?在调用android_atomic_release_cas函数之前,oldvalue和值就是从地址addr读出来的,如果在执行android_atomic_release_cas函数的时候,有其它地方也对地址addr进行操作,那么就会有可能出现*addr != oldvalue的情况,这时候就说明其它地方也在操作目标对象的强引用计数了,因此,这里就不能执行增加目标对象的强引用计数的操作了,它必须要等到其它地方操作完目标对象的强引用计数之后再重新执行,这就是为什么要通过一个while循环来执行了。

       第二种情况比较复杂一点,因为这时候目标对象可能还存在,也可能不存了,这要根据实际情况来判断。如果此时目标对象的强引用计数值等于INITIAL_STRONG_VALUE,说明此目标对象还从未被强指针引用过,这时候弱指针能够被提升为强指针的条件就为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {//强引用计数小于等于0,或者从未被强指针引用过
// we're now in the harder case of either:
// - there never was a strong reference on us
// - or, all strong references have been released
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {//如果生命周期是强引用计数导向
// this object has a "normal" life-time, i.e.: it gets destroyed
// when the last strong reference goes away
if (curCount <= 0) {//强引用计数为0,说明对象已被销毁,则升级失败
// the last strong-reference got released, the object cannot
// be revived.
decWeak(id);
return false;
}

// here, curCount == INITIAL_STRONG_VALUE, which means
// there never was a strong-reference, so we can try to
// promote this object; we need to do that atomically.
while (curCount > 0) {//从未被强指针引用过,则强引用计数+1
if (android_atomic_cmpxchg(curCount, curCount + 1,
&impl->mStrong) == 0) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation (e.g.: another thread has inc/decStrong'ed us)
curCount = impl->mStrong;
}

if (curCount <= 0) {如果多线程操作,入另一个线程销毁了这个对象,则升级失败
// promote() failed, some other thread destroyed us in the
// meantime (i.e.: strong count reached zero).
decWeak(id);
return false;
}
} else {//如果生命周期收弱引用计数导向,或者是BJECT_LIFETIME_FOREVER
// this object has an "extended" life-time, i.e.: it can be
// revived from a weak-reference only.
// Ask the object's implementation if it agrees to be revived
if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {//尝试升级,默认为true
// it didn't so give-up.
decWeak(id);
return false;
}
// grab a strong-reference, which is always safe due to the
// extended life-time.
curCount = android_atomic_inc(&impl->mStrong);//升级后强引用计数+1
}

       1) 即如果目标对象的生命周期只受到强引用计数控制或者在目标对象的具体实现中总是允许这种情况发生。怎么理解呢?如果目标对象的生命周期只受强引用计数控制(它的标志位mFlags为0),而这时目标对象又还未被强指针引用过,它自然就不会被delete掉,因此,这时候可以判断出目标对象是存在的;如果目标对象的生命周期受弱引用计数控制(OBJECT_LIFETIME_WEAK),这时候由于目标对象正在被弱指针引用,因此,弱引用计数一定不为0,目标对象一定存在;如果目标对象的生命周期不受引用计数控制(BJECT_LIFETIME_FOREVER),这时候目标对象也是下在被弱指针引用,因此,目标对象的所有者必须保证这个目标对象还没有被delete掉,否则就会出问题了。

       在后面两种场景下,因为目标对象的生命周期都是不受强引用计数控制的,而现在又要把弱指针提升为强指针,就需要进一步调用目标对象的onIncStrongAttempted来看看是否允许这种情况发生,这又该怎么理解呢?可以这样理解,目标对象的设计者可能本身就不希望这个对象被强指针引用,只能通过弱指针来引用它,因此,这里它就可以重载其父类的onIncStrongAttempted函数,然后返回false,这样就可以阻止弱指针都被提升为强指针。在RefBase类中,其成员函数onIncStrongAttempted默认是返回true的:

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

       2) 如果此时目标对象的强引用计数值小于等于0,那就说明该对象之前一定被强指针引用过,这时候就必须保证目标对象是被弱引用计数控制的(BJECT_LIFETIME_WEAK),否则的话,目标对象就已经被delete了。同样,这里也要调用一下目标对象的onIncStrongAttempted成员函数,来询问一下目标对象在强引用计数值小于等于0的时候,是否允计将弱指针提升为强指针。

       然后将强引用计数加一,还有一些收尾处理,函数的最后,如果此弱指针是允计提升为强指针的,并且此目标对象是第一次被强指针引用,还需要调整一下目标对象的强引用计数值。

       到这里弱指针就分析完了,它和强指针逻辑大同小异。

使用前提

       说了这么多原理,下面该看看智能指针该如何使用了。假设现在有一个类MyClass,如果要使用智能指针来引用这个类的对象,那么这个类需满足下列两个前提条件:
1:这个类是基类RefBase的子类或间接子类;
2:这个类必须定义虚构造函数,即它的构造函数需要这样定义:
       virtual ~MyClass();
       满足了上述条件的类就可以定义为Android智能指针了,定义方法和普通指针类似。比如       普通指针是这样定义:
       MyClass p_obj;
       智能指针是这样定义:
       sp p_obj;
       注意不要定义成sp
p_obj。这是初学者很容易犯的错误,这样其实相当于定义了一个指针的指针。尽管在语法上没有问题,但是最好不要这样定义。
       定义了一个智能指针的变量,就可以象普通指针那样使用它,包括赋值、访问对象成员、作为函数的返回值、作为函数的参数等。比如:

1
2
3
4
5
p_obj = new MyClass(); // 注意不要写成 p_obj = new sp<MyClass> 
sp<MyClass> p_obj2 = p_obj;
p_obj->func();
p_obj = create_obj();
some_func(p_obj);

       注意不要试图delete一个智能指针,即 delete p_obj。不要担心对象的销毁问题,智能指针的最大作用就是自动销毁不再使用的对象。不需要再使用一个对象后,直接将指针赋值为NULL即可:
       p_obj = NULL;
       上面说的都是强指针,弱指针的定义方法和强指针类似,但是不能通过弱指针来访问对象的成员。下面是弱指针的示例:

1
2
3
wp<MyClass> wp_obj = new MyClass(); 
p_obj = wp_obj.promote(); // 升级为强指针。不过这里要用.而不是->,真是有负其指针之名啊
wp_obj = NULL;

结语

       至此,智能指针部分就分析完了,我们不得不赞叹Google设计的强大。
结语

坚持技术分享,您的支持将鼓励我继续创作!