RAII - 安卓中的智能指针

RAII - 安卓中的智能指针

概念 sp wp RefBase

是什么
system/core/libutils/RefBase.cpp
system/core/libutils/include/utils/RefBase.h

system/core/libutils/StrongPointer.cpp
system/core/libutils/include/utils/StrongPointer.h

Android在标准库之外,自定义了以下两种智能指针类型:

  • 强指针sp(strong pointer),弱指针 wp(weak pointer)都是模板类

  • sp 和share_ptr概念相同。wp 和weak_ptr概念相同。spwp has-a RefBase

    • 弱指针仅仅记录对象的地址,不能通过弱指针来访问该对象。要想访问弱指针所指向的对象,需首先通过wp类所提供的promote()方法将弱指针升级为强指针。

    • 弱指针所指向的对象是有可能在其它地方被销毁的。如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况。

支持使用 spwp的对象,必须继承RefBase类。观察Android 中使用spwp的地方,传进来的模板参数都是继承自RefBase,例如:

// MediaPlayer是支持使用强指针和弱指针的对象
class MediaPlayer : public BnMediaPlayerClient,
                    public virtual IMediaDeathNotifier
{
   
public:
    MediaPlayer();
    ~MediaPlayer();
// 父类是 BnMediaPlayerClient 和 IMediaDeathNotifier,
// 多继承可能出现菱形继承,因此 虚继承自 RefBase
class IMediaDeathNotifier: virtual public RefBase
{
   
public:
    IMediaDeathNotifier() {
    addObitRecipient(this); }
    virtual ~IMediaDeathNotifier() {
    removeObitRecipient(this); }
    //随便找个,可以看到是继承RefBase,注意的是虚拟继承。
怎么用
// 使用智能指针的类必须继承自 RefBase
class Ressource : public RefBase {
    
    Ressource(int){
   }
    void test() {
   }
}

{
    // 作用域开始
    // 强引用 sp 构造函数有很多,这里简单列举两个
    Ressource* p = new Ressource(1);
    sp<Ressource> sp1(p);  // 1
    sp<Ressource> sp1 = new Ressource(1); // 2
    // sp::make 提供内存连贯性&缓存命中: 构建一个 sp 对象,其内部new一个资源对象,在将成员变量 m_ptr 指针指向它 
    static sp<Ressource> sp1 = sp<Ressource>::make(1); // 3
    // 通过强引用,操作指针指向的对象
    sp1->test();

    // 不能直接通过弱引用使用对象,升级为强指针再使用
    wp<Ressource> wp1(p); // 弱引用
    sp<Ressource> sp2 = wp1.promote();
    if(sp2 != NULL) {
     // sp重载了 != 原本spRessource2与 NULL 不是同一类型
        sp2->test();
    }
}   // 作用域结束,调用强引用对象的析构函数,释放掉 p 指针指向的内存
对比标准库
为什么不直接用标准库?

为什么不用标准库?

1、历史原因!C++11推出 shared_ptr 和 weak_ptr之前,Android就已经需要存在了。

2、特殊的功能:有些功能是针对特定使用场景定制的(显然不符合 C++ 标准)。

例如,特殊的接口:

template<typename T>
class sp {
   
	//! Special optimization for use by ProcessState (and nobody else).
    void force_set(T* other);
}
template<typename T>
void sp<T>::force_set(T* other) {
   
    other->forceIncStrong(this);
    m_ptr = other;
}

例如,智能指针有两种模式

  • 强引用控制 OBJECT_LIFETIME_STRONG (默认,且绝大多数情况下都是强引用控制)
  • 弱引用控制 OBJECT_LIFETIME_WEAK

可通过 void RefBase::extendObjectLifetime(int32_t mode) 函数切换。强/弱引用控制的切换,通常在被管理的目标类的构造函数中调用:

// system/core/libutils/include/utils/RefBase.h
enum {
   
    OBJECT_LIFETIME_STRONG  = 0x0000, //强引用控制
    OBJECT_LIFETIME_WEAK    = 0x0001, //弱引用控制
    // 掩码值,可使用 flag&OBJECT_LIFETIME_MASK 的形式来获取 flag 的最低位数值
    OBJECT_LIFETIME_MASK    = 0x0001
};

// frameworks/native/libs/binder/BpBinder.cpp
BpBinder::BpBinder(Handle&& handle) {
   
    // 调用函数修改智能指针模式
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
}

例如,神奇的功能:

static void check_not_on_stack(const void* ptr) {
   
实现上的区别

从被管理的资源的角度来看:

标准库的 shared_ptr,”管理它所持有的 裸指针“。

  • 被管理的资源——裸指针所指向的对象,可以是任意类型。
  • 处于"被管理状态",不会对资源本身造成什么影响,没有负担。

Android 实现的 sp,wp ”操作它所持有的 RefBase类的计数“。

  • 被管理的资源——必须是 RefBase类,即被管理要求原有类型必须继承 RefBase类。
  • 处于"被管理状态",意味着
    • 资源类RefBase 自身保存计数,保存的强引用计数归零后自己执行delete this
    • sp wp 会去操作,让资源类去管理自身保存的计数
优缺点

Android这样不使用 STD 智能指针的做法,

优点:总结来说就是,客制化的美~

  1. 允许进行根据需求自定义的修改/优化。

    前文介绍过的,特殊接口,强/弱引用控制

  2. 安卓fwk中普遍存在跨进程“引用资源”的的情况,RefBase 自行计数方便IPC情境下的管理。

  3. 方便且臃肿的 debug 函数

缺点:总结来说就是,增大对象本身的开销

  1. 对象占用内存增高—— 作为操作系统必要,但是其它场景不需要的开销

    诸如函数RefBase::getStrongCount()RefBase::weakref_type::trackMe()

  2. 资源类有时候需要虚继承 RefBase —— 为了解决菱形继承而不可避免的选择。因此相较于使用标准库的智能指针,每次更改计数都不得不承担虚函数表带来的开销。

  3. 不同于标准库这件事情本身就会影响可读性,引入新的复杂度。

你自己看看复杂不复杂,臃肿不臃肿

void RefBase::weakref_type::decWeak(const void* id)
{
   
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id); //删除弱引用
    //减少对象的弱引用计数,并且返回减少之前的值,存储在c中
    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); //计数减1
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    /*
    如果c不等于1则不用进行处理,
    如果c=1,说明弱引用计数为0,则强引用计数必定也为0,
    则考虑如何释放该对象
    */
    if (c != 1) return; //还有其他引用在用这个指针,直接返回
     /*
    释放方式取决于当
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值