Chinaunix首页 | 论坛 | 博客
  • 博客访问: 831979
  • 博文数量: 143
  • 博客积分: 455
  • 博客等级: 一等列兵
  • 技术积分: 861
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-03 00:11
文章分类

全部博文(143)

文章存档

2018年(10)

2017年(6)

2016年(28)

2015年(14)

2014年(67)

2013年(1)

2012年(17)

我的朋友

分类: Android平台

2014-08-18 17:07:18

转载自:
    在Android native编写代码时,会经常接触到sp、wp,sp并不是smart pointer的意思,而是strong point;wp就是weak pointer。这两个概念比较像JAVA中的强弱引用,使用sp和wp可以让人员不需要再关系内存的释放问题,防止内存泄露。下面先来看它们的类关系图:

.

要实现内存的自动释放,sp、wp必须结合RefBase这个类来使用,在Android中,大多数类的最上层基类都是RefBase类。我们举个简单的例子,然后顺着这个例子来分析RefBase、sp和wp四种不同的应用,并介绍其实现。<喎?"" target="_blank" class="keylink">vcD4KPHA+PC9wPgo8cHJlIGNsYXNzPQ=="brush:java;">class A : public RefBase { } 
上面定义一个类A,继承与RefBase,下面我们首先来看RefBases的构造函数:



1
2
3
4
5
6
7
8
9
10
11
12
RefBase::RefBase()
    : mRefs(newweakref_impl(this))
{
}
 
    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
    {
    }

在RefBase中,首先构造weakref_impl对象,在weakref_impl对mStong和mWeak进行强弱引用计数赋初始值,INITIAL_STRONG_VALUE是0X10000000,这里不直接赋初始值为0,是方便我们区分,0到底是初始化的值,还是在sp释放后再变为0,方便做不同的处理。


列举第一种应用:只有sp指针,没有wp指针的应用

{

sp

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

首先将实际的A对象的指针赋给m_ptr,然后调用A对象的incStrong方法,由上面的类图关系,我们知道这里会调用RefBase的incStrong方法:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
voidRefBase::incStrong(constvoid* id)const
{
    weakref_impl*constrefs = mRefs;
    refs->incWeak(id);
     
    refs->addStrongRef(id);
    constint32_t c = android_atomic_inc(&refs->mStrong);
    ALOG_ASSERT(c >0,"incStrong() called on %p after last strong ref", refs);
 
    if(c != INITIAL_STRONG_VALUE)  {
        return;
    }
 
    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    refs->mBase->onFirstRef();
}

这里首先调用weakref_impl的incWeak方法来增加弱指针引用数;addStrongRef用于debug版本,正式版中没有实现;android_atomic_inc是一个原子操作,增加refs->mStrong的强指针引用数,并返回原值;如果是第一次引用改对象,则还会调用A对象的onFirstRef方法。首先来看incWeak的实现:



1
2
3
4
5
6
7
voidRefBase::weakref_type::incWeak(constvoid* id)
{
    weakref_impl*constimpl = static_cast(this);
    impl->addWeakRef(id);
    constint32_t c = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >=0,"incWeak called on %p after last weak ref",this);
}

这里还是调用android_atomic_inc去增加weakref_impl的mWeak计数。经过构造函数,mStong和mWeak的计数都变成了1。当spA对象退出作用域以后,就会调用其析构函数来释放这个对象:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template
sp::~sp() {
    if(m_ptr)
        m_ptr->decStrong(this);
}
 
voidRefBase::decStrong(constvoid* id)const
{
    weakref_impl*constrefs = mRefs;
    refs->removeStrongRef(id);
    constint32_t c = android_atomic_dec(&refs->mStrong);
 
    ALOG_ASSERT(c >=1,"decStrong() called on %p too many times", refs);
    if(c ==1) {
        refs->mBase->onLastStrongRef(id);
        if((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            deletethis;
        }
    }
    refs->decWeak(id);
}

sp对象的析构函数调用RefBase的decStrong来减少强弱引用指针计数。weakref_impl的removeStrongRef用于debug版本;调用android_atomic_dec减少mStrong计数并返回原值,如果mStrong之前为1了,这是再减少,说明已经没有其它sp指针引用了,这时候首先调用A对象的onLastStrongRef方法,如果Flag设定的是当前对象的生命周期由sp指针决定(这也是default的设定),则释放掉A对象;然后调用weakref_impl的decWeak去减少弱引用指针计数:



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
voidRefBase::weakref_type::decWeak(constvoid* id)
{
    weakref_impl*constimpl = static_cast(this);
    impl->removeWeakRef(id);
    constint32_t c = android_atomic_dec(&impl->mWeak);
    ALOG_ASSERT(c >=1,"decWeak called on %p too many times",this);
    if(c !=1)return;
 
    if((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
        if(impl->mStrong == INITIAL_STRONG_VALUE) {
            delete impl->mBase;
        }else{
            delete impl;
        }
    }else{
        impl->mBase->onLastWeakRef(id);
        if((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            delete impl->mBase;
        }
    }
}



weakref_impl的removeWeakRef方法也是用于debug版本;然后调用android_atomic_dec去减少mWeak计数,如果mWeak之前不等于1,表示还有其它的wp引用,这里就直接返回。如果这里的mWeak等于1,说明已经没有其它sp和wp的引用了,所以这里要去释放A对象和weakref_impl对象。

函数:


1
2
3
4
5
6
7
8
9
10
11
12
13
RefBase::~RefBase()
{
    if(mRefs->mStrong == INITIAL_STRONG_VALUE) {
        delete mRefs;
    }else{
        if((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
            if(mRefs->mWeak ==0) {
                delete mRefs;
            }
        }
    }
    const_cast(mRefs) = NULL;
}

如果没有初始化过sp对象,则删除mRefs对象;如果Flag设定当前对象的生命周期由wp指针决定并且mWeak计数为0,也删除mRefs对象。



列举第二种应用:只有wp指针,没有sp指针的应用


{

wp

1
2
3
4
5
6
7
8
template
wp::wp(constsp& other)
    : m_ptr(other.m_ptr)
{
    if(m_ptr) {
        m_refs = m_ptr->createWeak(this);
    }
}

首先将A对象的指针赋予给m_ptr,可见在sp和wp中都保存有A对象的实际指针,但wp中并没有重载"->",所以wp并不能直接调用A对象的方法,并且由前面sp的知识,我们知道,在decStrong的时候,有可能A对象会被释放,并不会care 是否存在wp的引用。然后调用A对象的createWeak方法,实际上是调用RefBase的这个方法。注意的是在wp中,m_refs是weakref_type的指针;而在RefBase中,mRefs是weakref_impl的指针,所以在下面的代码返回时要注意类型的转型。


1
2
3
4
5
6
7
8
9
10
11
12
13
RefBase::weakref_type* RefBase::createWeak(constvoid* id)const
{
    mRefs->incWeak(id);
    returnmRefs;
}
 
voidRefBase::weakref_type::incWeak(constvoid* id)
{
    weakref_impl*constimpl = static_cast(this);
    impl->addWeakRef(id);
    constint32_t c = android_atomic_inc(&impl->mWeak);
    ALOG_ASSERT(c >=0,"incWeak called on %p after last weak ref",this);
}

这里只会增加mWeak 计数,这是mStrong等于INITIAL_STRONG_VALUE,mWeak等于1。当wpA退出作用域后,调用wp的析构函数:



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

decWeak函数我们上面讲过,如果Flag设定当前对象的生命周期由sp指针决定,并且之前没有初始化过任何sp对象,则直接删除A对象;并在RefBase的析构函数中取释放mRefs对象。



列举第三种应用:既有sp指针,又有wp指针的应用


{

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

这里调用weakref_type的attemptIncStrong方法去尝试增加mStrong计数:



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
bool RefBase::weakref_type::attemptIncStrong(constvoid* id)
{
    incWeak(id);
     
    weakref_impl*constimpl = static_cast(this);
    int32_t curCount = impl->mStrong;
 
    while(curCount >0&& curCount != INITIAL_STRONG_VALUE) {
        if(android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) ==0) {
            break;
        }
        curCount = impl->mStrong;
    }
     
    if(curCount <=0|| curCount == INITIAL_STRONG_VALUE) {
        if((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
            if(curCount <=0) {
                decWeak(id);
                returnfalse;
            }
 
            while(curCount >0) {
                if(android_atomic_cmpxchg(curCount, curCount +1,
                        &impl->mStrong) ==0) {
                    break;
                }
                curCount = impl->mStrong;
            }
 
            if(curCount <=0) {
                decWeak(id);
                returnfalse;
            }
        }else{
            if(!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
                decWeak(id);
                returnfalse;
            }
            curCount = android_atomic_inc(&impl->mStrong);
        }
 
        if(curCount >0&& curCount < INITIAL_STRONG_VALUE) {
            impl->mBase->onLastStrongRef(id);
        }
    }
     
    impl->addStrongRef(id);
 
    curCount = impl->mStrong;
    while(curCount >= INITIAL_STRONG_VALUE) {
        if(android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE,
                &impl->mStrong) ==0) {
            break;
        }
        curCount = impl->mStrong;
    }
 
    returntrue;
}

首先调用incWeak来增加mWeak计数,因为这里需要获取sp指针,在sp的构造函数我们知道,会同时增加mWeak和mStrong值。然后根据mStong值分两种情况讨论:


1. 当前面存在sp的引用,即curCount > 0 && curCount != INITIAL_STRONG_VALUE,这时直接让mStrong加1。

2.当前面不存在sp的引用,需要结合Flag去判断。又分为以下几种情况:

一. Flag = OBJECT_LIFETIME_STRONG,并且curCount等于0。说明之前的sp对象已经释放,由前面的知识我们知道,在释放sp对象的同时也会释放对象A,所以这里调用decWeak来释放前面增加的一次mWeak值并返回false

二.Flag = OBJECT_LIFETIME_STRONG,并且curCount = INITIAL_STRONG_VALUE,说明前面没有sp引用,这时我们可以增加mStrong值。

三.Flag = OBJECT_LIFETIME_WEAK,并且curCount <= 0 || curCount == INITIAL_STRONG_VALUE,则调用RefBase的onIncStrongAttempted去尝试增加mStrong值

当上面任何一种情况增加了mStrong值以后,mSrong的值可能大于INITIAL_STRONG_VALUE,我们需要去修正mStrong,就是通过减去INITIAL_STRONG_VALUE计算。当attemptIncStrong返回true时,promote方法就会调用sp的set_pointer方法去设置StrongPointer中的实际A对象的指针。接下来就可以通过sp调用相关的方法了。

阅读(969) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~