之前文章说了智能指针中的轻量级指针,对于轻量级指针还是比较好理解的,现在这篇文章就来说一下强引用指针。
具体说强引用指针之前,先说一个场景,有助于对为什么要有强引用指针以及接下去要说的弱引用指针有个比较直观的理解。
做android应用的同学肯定在java的开发中遇到过强引用的概念,其实android这里的基本概念是差不多的,这也是垃圾回收中的一个典型场景。试想一下,有2个对象A和B,他们之间是相互引用的,如果现在对于他们来说都不需要引用对方了,那么是不是能够释放呢?如果按照正常的理解,A需要释放,前体条件是没有其他对象引用A,但是现在我们这里的场景是B引用了A,所以A不能被释放。同理如果B要被释放,必须没有对象引用它,可惜现在A引用了B,所以导致了现在A和B他们都不能被释放,这样就产生问题了,如果有大量这样的对象存在内存中,而得不到释放,那么比如会导致内存的溢出,所以需要引入强弱指针的概念来解决这类问题。
在使用强弱指针来解决这类场景的方案中,一般这种相互关联的对象会被设置为主-从的对象,主对象使用强指针来引用从对象,相对的从对象使用弱指针来引用主对象。而每个对象对于生命周期的控制在android的智能指针中会被划分为三种,即只受强引用指针影响,同时受强弱引用指针影响,完全不受强弱引用指针影响。
具体来说,这三种类别在释放对象时候的判断分别如下:
1.只受强引用指针影响。只要强引用指针的计数器值为0,就可以释放对象。
2.同时受强弱引用指针影响。需要强引用和弱引用指针的计数器都为0,才能释放对象。
3.完全不受强弱引用指针影响。对象不会自动释放,只有开发者手动才会释放对象
其实只要理解了上面三条,就可以基本理解强弱指针的大概意思了,好了,下面我们开始具体分析代码,首先还是把强引用指针对象的基类代码看一下,基类中包含着计数器对象,下面来看下。
```c++
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);
}
protected:
RefBase();
virtual ~RefBase();
enum {
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_FOREVER = 0x0003
};
void extendObjectLifetime(int32_t mode);
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这个类就是所有需要使用强弱引用指针都需要继承的基类,这个类的public方法中大家可以看到incStrong,decStrong,getStrongCount这些方法,看名字想必大家也容易猜到这些就是增加,减少以及得到强引用计数器的方法。
然后往后看到private中weakref_impl* const mRefs这一个变量,这个变量是具体的计数器类,他是内部类weakref_type的一个实现类,我们看下代码:
```c++
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
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*/) { }
void printRefs() const { }
void trackMe(bool, bool) { }
#else
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)
{
//LOGI("NEW weakref_impl %p for RefBase %p", this, base);
}
~weakref_impl()
{
LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!");
LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!");
}
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);
}
void trackMe(bool track, bool retain)
{
mTrackEnabled = track;
mRetain = retain;
}
void printRefs() const
{
String8 text;
{
AutoMutex _l(const_cast<weakref_impl*>(this)->mMutex);
char buf[128];
sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
text.append(buf);
printRefsLocked(&text, mStrongRefs);
sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
text.append(buf);
printRefsLocked(&text, mWeakRefs);
}
{
char name[100];
snprintf(name, 100, "/data/%p.stack", this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND);
if (rc >= 0) {
write(rc, text.string(), text.length());
close(rc);
LOGD("STACK TRACE for %p saved in %s", this, name);
}
else LOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this,
name, strerror(errno));
}
}
private:
struct ref_entry
{
ref_entry* next;
const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack stack;
#endif
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;
#if DEBUG_REFS_CALLSTACK_ENABLED
ref->stack.update(2);
#endif
ref->next = *refs;
*refs = ref;
}
}
void removeRef(ref_entry** refs, const void* id)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* ref = *refs;
while (ref != NULL) {
if (ref->id == id) {
*refs = ref->next;
delete ref;
return;
}
refs = &ref->next;
ref = *refs;
}
LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!",
id, mBase, this);
}
}
void printRefsLocked(String8* out, const ref_entry* refs) const
{
char buf[128];
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
sprintf(buf, "\t%c ID %p (ref %d):\n",
inc, refs->id, refs->ref);
out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
out->append(refs->stack.toString("\t\t"));
#else
out->append("\t\t(call stacks disabled)");
#endif
refs = refs->next;
}
}
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;
#if 0
void addRef(KeyedVector<const void*, int32_t>* refs, const void* id)
{
AutoMutex _l(mMutex);
ssize_t i = refs->indexOfKey(id);
if (i >= 0) {
++(refs->editValueAt(i));
} else {
i = refs->add(id, 1);
}
}
void removeRef(KeyedVector<const void*, int32_t>* refs, const void* id)
{
AutoMutex _l(mMutex);
ssize_t i = refs->indexOfKey(id);
LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id);
if (i >= 0) {
int32_t val = --(refs->editValueAt(i));
if (val == 0) {
refs->removeItemsAt(i);
}
}
}
void printRefs(const KeyedVector<const void*, int32_t>& refs)
{
const size_t N=refs.size();
for (size_t i=0; i<N; i++) {
printf("\tID %p: %d remain\n", refs.keyAt(i), refs.valueAt(i));
}
}
mutable Mutex mMutex;
KeyedVector<const void*, int32_t> mStrongRefs;
KeyedVector<const void*, int32_t> mWeakRefs;
#endif
#endif
};
```
这个实现类看起来比较多,不用怕,我们看主要的就可以,其实最重要的就是开头的几个变量:
```c++
volatile int32_t mStrong;
volatile int32_t mWeak;
RefBase* const mBase;
volatile int32_t mFlags;
```
这里分别说明下,mStrong就是强引用指针的计数器,mWeak就是弱引用指针的技术器,mBase指向那个需要被智能指针使用的对象,mFlags是对象生命周期的控制方式。
另外注意下这里构造函数里面赋于的初始值:
```c++
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
{
}
```
可以看到强引用指针的初始值是INITIAL_STRONG_VALUE,它的值被定义为 #define INITIAL_STRONG_VALUE (1<<28),是不是感到很奇怪,竟然不是1,你看下下面弱引用指针的计数器值mWeak是0,强引用这里为什么不是呢?这里先卖个关子,下面通过代码的逻辑就比较清楚了。另外mFlags的初始值是0,这里0表示,这个对象的声明周期控制方式只受强引用影响,记得文章最开头说过有3种声明周期控制方式吗,mFlags这个变量就是用来记录这个的。
然后我们来看下RefBase的构造函数:
```c++
RefBase::RefBase()
: mRefs(new weakref_impl(this))
{
// LOGV("Creating refs %p with RefBase %p\n", mRefs, this);
}
```
可以看到就是直接new了一个weakref_impl给mRefs变量,这个比较容易理解,就不多说了。
好了,这个基类相关的就先介绍到这里,下面开始看强引用指针的类,如下:
```c++
template <typename T>
class sp
{
public:
typedef typename RefBase::weakref_type weakref_type;
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;
// Optimization for wp::promote().
sp(T* p, weakref_type* refs);
T* m_ptr;
};
```
这个在轻量级指针中也用过,现在再说下和强引用有关的部分,先还是看下sp的构造方法
```c++
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);
}
```
两个构造方法其实意思是一样的,我们看到构造方法会把引用的对象复制给m_ptr,m_ptr是一个泛型,指向那个被引用的对象,也是最开始上面说的那个继承基类RefBase的对象,随后会调用RefBase中的incStrong,即增加强引用计数器。我们看下incStrong方法实现:
```c++
void RefBase::incStrong(const void* id) const // 强引用增加,可以看出,一旦增加一个强引用,弱引用首先也会被增加,所以弱引用>=强引用
{
weakref_impl* const refs = mRefs;
refs->addWeakRef(id);
refs->incWeak(id); //先增加弱引用
refs->addStrongRef(id);
const int32_t c = android_atomic_inc(&refs->mStrong); //增加强引用,返回的是增加之前的数量。即如果开始是0,现在增加了1,值虽然变成了1,但是返回值是0
LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
if (c != INITIAL_STRONG_VALUE) { // 如果不是第一次,到这里就可以return了
return;
}
android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); // 如果是第一次,需要把初值调整为1,因为INITIAL_STRONG_VALUE初值不是1
const_cast<RefBase*>(this)->onFirstRef(); // 处理些善后工作
}
```
上面我在关键语句做了注释,可以看到只要增加一个强引用,比如先增加一个弱引用,所以弱引用是大于等于强引用的.然后会增加强引用。这里有个常量INITIAL_STRONG_VALUE,记得上面介绍weakref_impl类的时候说过这个常量吗,他代表强引用计数器的初值,当时还是为什么初始值不是0,这个再下面会介绍,这里反正大家知道了如果c的值为INITIAL_STRONG_VALUE说明这个类初始化都没有被引用过,现在我们把计数器加1了,但是在INITIAL_STRONG_VALUE这个值上加1,数字就不对了,所以在加1后又执行了android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);这句代码,把值重置为1,这下就正确了。总的来说sp的构造方法就是把强弱引用指针都加1。
构造方法一旦被调用,那就说明某个对象被别人引用了,那么他里面的强弱引用都会加1,那么指针指针是怎么自动销毁对象的呢,这个我们就要看sp的析构函数了:
```c++
template<typename T>
sp<T>::~sp()
{
if (m_ptr) m_ptr->decStrong(this);
}
```
析构函数很简单,就是调用RefBase的decStrong方法,我们在看下decStrong方法:
```c++
void RefBase::decStrong(const void* id) const // 强引用减一
{
weakref_impl* const refs = mRefs; // mRefs是sp中的weakref_impl变量
refs->removeStrongRef(id);
const int32_t c = android_atomic_dec(&refs->mStrong); // 强引用指针-1,c是减一前的值
#if PRINT_REFS
LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
if (c == 1) { // 等于1说明强引用已经没有了,可以释放
const_cast<RefBase*>(this)->onLastStrongRef(id);
if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { // 可以理解为只受强引用控制,那么如果没有强引用了,就会释放
delete this;
}
}
refs->removeWeakRef(id);
refs->decWeak(id);
}
```
可以看到decStrong函数会调用android_atomic_dec(&refs->mStrong)来把强引用计数器减一,然后下面如果进入if (c == 1)这个分支里说明已经没有强引用了(android_atomic_dec方法返回的是减一前的引用数量,所以c == 1即表示引用为0了),我们看到有个这个判断if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK),这个表示什么意思呢?还是回到最开始文章说的三种生命周期控制吗,mFlags的值就是表示这三种情况,初始化的时候前面看到会把Flags初始化为0,另外在前面Refbase中有定义,如下:
```c++
enum {
OBJECT_LIFETIME_WEAK = 0x0001, // 同时受强弱引用影响
OBJECT_LIFETIME_FOREVER = 0x0003 // 强弱引用都不受影响
};
```
好了,再回到这句代码中refs->mFlags&OBJECT_LIFETIME_WEAK,mFlags和OBJECT_LIFETIME_WEAK做与操作,OBJECT_LIFETIME_WEAK值是1,所以只要看mFlags的最低位是否为1,如果为1就是等于OBJECT_LIFETIME_WEAK的,为0的话这个if分支就进入执行了。我们说过mFlags就三种情况0,OBJECT_LIFETIME_WEAK,OBJECT_LIFETIME_FOREVER,后两种的最低位都是1,只有0的时候才会进入这个if分支,0即表示只受强引用影响,所以这里这里代码表示的意思就是如果对象的强引用指针计数器是0了,并且该对象只受强引用影响,那么就会释放该对象,这样就比较清楚了吧。
另外说一下代码中类似removeWeakRef这种尾巴带Ref的不影响主流程的执行,我们可以忽略,看最后一句代码refs->decWeak(id),我们跳转到具体代码的地方:
```c++
void RefBase::weakref_type::decWeak(const void* id) // 弱引用减一
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
const int32_t c = android_atomic_dec(&impl->mWeak); // 弱引用减1
LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
if (c != 1) return; // 如果还有弱引用就return
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { // 只有0才会进这里,可以理解为只受强引用控制。
if (impl->mStrong == INITIAL_STRONG_VALUE) // 说明没有被强引用引用过,那么就会把强引用没用过的对方释放掉
delete impl->mBase;
else {
// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl; // 到这里说被强引用引用过,那么之前decStrong方法的地方,肯定已经被释放掉了,现在既然弱引用也是0了,那么也可以释放imp了,为什么不在~RefBase()析构中呢,因为进入这个分支肯定是之前impl->mBase已经被执行了析构了,所以那时候弱引用可能还不是0,所以在这里在del下
}
} else {
impl->mBase->onLastWeakRef(id);
if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { // 到这里不等于OBJECT_LIFETIME_FOREVER,说明是既受强引用也受弱引用,那么就可以把强引用的对象释放掉,因为如果进这里分支,前面decStrong中释放强引用的地方肯定没有释放过,不过这里为什么不释放impl呢?因为impl是Refbase中new出来的,所以会在~RefBase()中del
delete impl->mBase;
}
} // 如果以上分支都不是,说明既不受强引用,也不说弱引用,指针指针没有用,需要用户自己手动释放
}
```
这个方法是弱引用指针减一,然后当减到为0时,又有了一些if/else的判断,我们看下他的逻辑。这个同样分为两部分,如果只受强引用影响会单独处理,其他的再单独处理。
先看如果只受强引用影响,if (impl->mStrong == INITIAL_STRONG_VALUE)表示这个对象从来没有被引用过,所以不可能被释放,记得前面强引用释放的条件之一是强引用计数为0吗,这个不是0,所以肯定没有释放过,所以既然没有释放,有没有强引用,弱引用现在又是0了,所以可以释放了。进入else之后,说明肯定有被强引用引用过,记得之前说的弱引用计数肯定是大于等于强引用计数的,现在既然弱引用计数为0的,强引用计数肯定不会大于0,而现在又不是INITIAL_STRONG_VALUE这个值,所以说明强引用肯定是0,那么之前decStrong方法中肯定已经被释放掉了,所以现在只有需要把impl是否掉就可以了(为什么这里要单独释放impl,其实impl会在Refbase中根据判断被释放,这个等下单独说一下)。好了只受强引用的条件说完了,下面说下非只受强引用的情况。
非只受强引用的情况有2种,即同时受强弱引用影响和都不受影响。if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER)这个判断条件说明是同时受强弱指针影响的,OBJECT_LIFETIME_FOREVER的值是0x0003,而现在的值只可能是0x0001和0x0003,要这里if成立,只有mFlags为0x0001,即同时受强弱引用影响,这种情况下,只有强引用和弱引用都为0了,才会释放,目前代码执行到这里确实是都为0了,所以可以释放。
最后如果mFlags是OBJECT_LIFETIME_FOREVER说明强弱都不受影响,所以只有开发者手动释放,我们就不用管了。
说一下前面delete impl这句,我们先看下RefBase的析构函数:
```c++
RefBase::~RefBase()
{
if (mRefs->mWeak == 0) { // 弱引用为0,删除impl
delete mRefs;
}
}
```
可以看到一旦RefBase析构执行,在弱引用为0的情况下会释放mRefs,mRefs就是impl,所以在弱引用计数为0时,只要释放了对象,就会自动释放impl,但是在强引用计数为0时释放对象,不能确保弱引用计数是为0的,因为弱引用计数是大于等于强引用计数的,所以在上面代码中,我们在确定强引用已经被释放的情况下,需要手动在执行下delete impl来确保能够正确是否impl对象。
好了,整个强引用指针说完了,相对轻量级指针来说是复杂了很多,但是这样设计确实还是比较巧妙的,我们看这些设计过程确实也是一个学习提升的过程,最后还有一个弱引用指针,我们下一篇文章见。
Android智能指针之强引用指针