普通指针的使用隐患和智能指针的解决办法

普通指针在使用过程中的三个隐患:

  1. 指针没有初始化。
  2. 指针没有释放。
  3. 释放后没有置空。

智能指针的解决办法:

  1. 构造函数中初始化。
  2. 使用计数器,计数器为0时释放。
  3. 将指针封装在智能指针中,析构时释放。

标准库中的智能指针为shared_ptr,而android Binder相关代码使用wp,sp作为智能指针。

android中的智能指针

计数器由object自身持有

计数器并非由智能指针拥有(这是我之前一直弄错的地方)。如果计数器由智能指针拥有,那么当有多个智能指针都由该object初始化的时候,它们的计数值不能共享,也就无法确定该object究竟被引用了多少次。

解决办法就是让计数器由object自身持有,这样只要在初始化智能指针的时候更新object持有的计数器,计数值就自然而然地被所有智能指针共享了。每个智能指针都可以从object的计数器中知道该object还剩多少个引用,从而决定是否释放。

android中定义了统一的父类“RefBase”和“LightRefBase”,所有有计数要求的类型都继承于这两个类。

这是sp和wp的实现方法,暂不清楚stl中是怎么实现智能指针的。

另外,sp中重载了->操作符,sp指针->的效果等价于原指针->的效果

弱指针wp的构造

弱指针是为了避免智能指针引用中的“死锁”而产生的,“死锁”现象即两个类的成员变量中均有指向对方的智能指针,这样就会导致这两个类的智能指针引用计数永远都不为零。

弱引用有两个特点:

  1. 当强引用为0时,无论弱引用是多少,对象都会被delete(android中有例外?)。
  2. 弱指针必须先升级成强指针,才能访问它所指的对象。

wp的参数为指针的构造函数如下:

//frameworks\rs\cpp\util\RefBase.h
template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) m_refs = other->createWeak(this);
}

构造函数把指针赋给自己的成员m_ptr,然后以自身作为参数调用了createWeak函数,createWeak函数属于前面提到的父类RefBase:

//android-7.1.1_r1\system\core\libutils\RefBase.cpp
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

createWeak将wp指针作为id参数调用了mRefs的incWeak函数(从之后的代码来看,id仅是用来调试用的,这里可以忽略),并将mRef返回。mRef将被赋值给wp的m_refs。
mRef的类型是weakref_impl,而wp的m_refs成员的类型是weakref_type,weakref_impl继承weakref_type,从名字就可以看到前者是后者的实现类,可以把这两者当作相同类型看待。
weakref_impl非常重要,截取weakref_impl的定义如下:

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    std::atomic<int32_t>    mStrong;
    std::atomic<int32_t>    mWeak;
    RefBase* const          mBase;
    std::atomic<int32_t>    mFlags;

#if !DEBUG_REFS

    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
    {
    }
...
}

其中的mStrong和mWeak就是强弱引用计数,mBase是weakref_impl的从属类RefBase的指针,之所以这样设计是为了便于释放RefBase。从这里可以看出weakref_impl即是统一父类RefBase的计数器。接下来看incWeak函数:

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);
    const int32_t c __unused = impl->mWeak.fetch_add(1,
            std::memory_order_relaxed);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

第一行代码用于强制类型转换,将weakref_type强制转化成weakref_impl,第二行是debug代码,release版本中addWeakRef并没有实现。第三行即是将weakref_impl的mWeak+1,这里实现了计数器的自增
这里的写法非常奇特,是我以前没见过的写法,incWeak属于weakref_type,其内部实现却调用了子类weakref_Impl的函数,不知道为什么要这样设计。(有点好奇这样的写法是怎么通过编译的)

各个类的关系如下:
在这里插入图片描述

强指针sp的构造

强指针的代码比弱指针的代码简单,它的构造函数如下

//\frameworks\rs\cpp\util\StrongPointer.h
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
  {
    if (other) other->incStrong(this);
  }

直接调用了”统一父类“RefBase的incStrong函数。这里注意和弱指针的不同,弱指针的incWeak在weakref_type类中,而强指针的incStrong在RefBase类中。继续看incStrong:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
    ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
            std::memory_order_relaxed);
    // A decStrong() must still happen after us.
    ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
    refs->mBase->onFirstRef();
}

就是强弱引用计数自增。后面的代码是处理第一次引用的情况的,可以忽略。

强弱指针的删除

引用的自减由decStrong和decWeak完成,和前面一样,decStrong属于RefBase,而decWeak属于weakref_type。
强指针的析构会调用decStrong先使强引用减一,再调用decWeak使弱引用减一(注意先后顺序)。弱指针的析构会调用decWeak使弱引用减一。当强弱引用为0时,就要判断是否要释放指针和计数器,即RefBase和weakref_impl。考虑以下几种情况:

  1. 强引用为0,弱引用不为0。强指针的析构可能会导致该情况的发生。此时decStrong会释放RefBase,但decWeak不会释放weakref_impl,即wp的m_ref成员依然有效。
  2. 弱引用为0,强引用为0,且指针曾被强引用过。这种情况只可能发生在情况1之后,此时RefBase已被释放,decWeak只需释放weakref_impl即可。
  3. 弱引用为0,强引用为0,且指针未被强引用过。这种情况下,由于没有强引用,也就没有强指针的析构,decStrong不会被调用,故RefBase释放的任务由decWeak完成。而RefBase的析构函数中,当弱引用为0时,会释放weakref_impl,这就完成了指针的计数器的释放。

虽然weakref_impl是由RefBase创建的,但在情况1和情况2中,它却是由自己完成自己的释放(在weakref_type的incWeak中完成释放,难道这就是分weakref_impl和weakref_type的理由?),只有在情况3中它才被RefBase释放。这是由于强引用为0时,弱引用不一定为0,所以此时计数器不能随着RefBase释放。只有当强弱引用都为0时才能释放计数器。
理解了以上概念后,再看代码就不难了:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
#if PRINT_REFS
    ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        refs->mBase->onLastStrongRef(id);
        int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
            // Since mStrong had been incremented, the destructor did not
            // delete refs.
        }
    }
    // Note that even with only strong reference operations, the thread
    // deallocating this may not be the same as the thread deallocating refs.
    // That's OK: all accesses to this happen before its deletion here,
    // and all accesses to refs happen before its deletion in the final decWeak.
    // The destructor can safely access mRefs because either it's deleting
    // mRefs itself, or it's running entirely before the final mWeak decrement.
    refs->decWeak(id);
}

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);
    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;
    atomic_thread_fence(std::memory_order_acquire);

    int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
    if ((flags&OBJECT_LIFETIME_MASK) == 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.load(std::memory_order_relaxed)
                == 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 {
        // This is the OBJECT_LIFETIME_WEAK case. The last weak-reference
        // is gone, we can destroy the object.
        impl->mBase->onLastWeakRef(id);
        delete impl->mBase;
    }
}

参考资料:深入理解Android内核设计思想

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐