Android智能指针SP WP

news2024/11/30 0:29:55

1.概述

Android的C++部分代码中有大量的sp/wp存在,意思是strong pointer和weak pointer,看字面意思就是指针相关的东西。C++是通过new和delete进行内存的分配和释放的,但是有时候开发者会忘记使用delete导致内存泄露,所以Android中就创建了sp/wp等,用于避免内存泄露和提高开发效率。
强指针通过引用计数来记录有多少使用者在使用一个对象,如果所有使用者都放弃了对该对象的引用,则该对象将被自动销毁。弱指针仅仅记录该对象的地址,不能通过弱指针来访问该对象,也就是说不能通过弱智真来调用对象的成员函数或访问对象的成员变量。要想访问弱指针所指向的对象,需首先通过wp所提供的promote()方法将弱指针升级为强指针。弱指针所指向的对象是有可能在其它地方被销毁的,如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况
在这里插入图片描述

2. sp

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

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other) {
        check_not_on_stack(other);
        other->incStrong(this);
    }
}
template <typename T>
template <typename U>
sp<T>::sp(U* other) : m_ptr(other) {
    if (other) {
        check_not_on_stack(other);
        (static_cast<T*>(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);
}
template <typename T>
sp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {
    other.m_ptr = nullptr;
}
template<typename T> template<typename U>
sp<T>::sp(const sp<U>& other)
        : m_ptr(other.m_ptr) {
    if (m_ptr)
        m_ptr->incStrong(this);
}
template<typename T> template<typename U>
sp<T>::sp(sp<U>&& other)
        : m_ptr(other.m_ptr) {
    other.m_ptr = nullptr;
}
初始化sp中的指针m_ptr
m_ptr调用incStrong函数,进行计数加1
template <typename T>
template <typename... Args>
sp<T> sp<T>::make(Args&&... args) {
    T* t = new T(std::forward<Args>(args)...);
    sp<T> result;
    result.m_ptr = t;
    t->incStrong(t);  // bypass check_not_on_stack for heap allocation
    return result;
}
sp实际就是一个引用,不会单独的申请内存,对引用对象的初始化是在make中
初始化m_ptr指针
引用计数加一

3.示例

在Android中,大多数类的最上层基类都是RefBase类。我们举个简单的例子,然后顺着这个例子来分析RefBase、sp和wp四种不同的应用,并介绍其实现

3.1 只有sp指针,没有wp指针的应用

class A : public RefBase
{
}

上面定义一个类A,继承与RefBase,下面我们首先来看RefBases的构造函数:

RefBase::RefBase()
    : mRefs(new weakref_impl(this))
{
}
 
    explicit weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(OBJECT_LIFETIME_STRONG)
    {
    }

explicit关键字通常用来将构造函数标记为显式类型转换,即在创建对象的时候不能进行隐式类型转换。
在RefBase中,首先构造weakref_impl对象,在weakref_impl对mStong和mWeak进行强弱引用计数赋初始值,INITIAL_STRONG_VALUE是0X10000000,这里不直接赋初始值为0,是方便我们区分,0到底是初始化的值,还是在sp释放后再变为0,方便做不同的处理。

{

    sp<A> spA(new A);

}

首先来看sp的构造函数:

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}

首先将实际的A对象的指针赋给m_ptr,然后调用A对象的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 = 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的实现

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);
    const int32_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对象退出作用域以后,就会调用其析构函数来释放这个对象:

template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}
 
void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_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) {
            delete this;
        }
    }
    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去减少弱引用指针计数:

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);
    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对象。
来看判断是否释放的逻辑,如果Flag设定当前对象的生命周期由sp指针决定,并且之前没有初始化过任何sp对象,则直接删除A对象;如果之前由初始化过sp对象,则删除weakref_impl本身,A对象会在RefBase的decStrong中被释放。如果Flag设定当前对象的生命周期由wp指针决定,则首先调用A对象的onLastWeakRef方法,然后删除对象A。在删除对象A的时候,都会调用RefBase的析构函数,我们再来分析RefBase的系统函数:

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<weakref_impl*&>(mRefs) = NULL;
}

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

3.2 只有wp指针,没有sp指针的应用.

{

    wp<A> wpA(new A);

}

首先来看wp的构造函数:


template<typename T>
wp<T>::wp(const sp<T>& 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的指针,所以在下面的代码返回时要注意类型的转型。

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}
 
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);
    const int32_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的析构函数:

template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this);
}

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

3.3 既有sp指针,又有wp指针的应用

{

   sp<A> spA(new A)

    wp<A> wpA(spA);

}

从上面它们的构造函数我们知道,这是mStong等于1,mWeak等于2。在spA和wpA退出作用域时,首先调用wp的析构函数,再调用sp的析构函数。在wp析构函数中,只会减少mWeak计数为1,然后就然后了。再到sp的析构函数中,就和我们前面介绍的第一种应用一样了。

3.4 wp指针如果调用对象的方法

前面说过在wp中并没有重载"->",所以wp并不能直接调用A对象的方法,并且由前面sp的知识,我们知道,在decStrong的时候,有可能A对象会被释放,所以在wp中想要调用A对象的方法,必须获得sp指针,这是通过wp的promote方法实现的:

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

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

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) {
        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);
                return false;
            }
 
            while (curCount > 0) {
                if (android_atomic_cmpxchg(curCount, curCount + 1,
                        &impl->mStrong) == 0) {
                    break;
                }
                curCount = impl->mStrong;
            }
 
            if (curCount <= 0) {
                decWeak(id);
                return false;
            }
        } else {
            if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
                decWeak(id);
                return false;
            }
            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;
    }
 
    return true;
}

首先调用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调用相关的方法了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/589755.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计师们都在用的AI作图神器,你还不抓紧入手一款

人工智能在机器和计算机控制的机器人中模拟人类智能过程。这允许计算机系统执行繁重的任务&#xff0c;帮助人类专注于更重要的事情。因此&#xff0c;多年来&#xff0c;工作场所对 AI 集成的需求不断增加。 同样&#xff0c;人工智能正迅速成为设计行业的一部分。在平面设计…

MyBatis与Spring的整合

学习目标&#xff1a; 掌握Spring与MyBatis的集成 掌握使用SqlSessionTemplate实现整合 掌握使用MapperFactoryBean实现整合 掌握Spring的事务切面实现声明式事务处理 掌握使用注解实现声明式事务处理 学习内容&#xff1a; 1.整合思路梳理 思路梳理 以上流程可以全部移…

RHCE 作业二

1. 第一步&#xff1a;配置服务端server 1>安装chrony [rootserver ~]# yum install chrony -y 2>编辑配置文件&#xff0c;修改为阿里的时间服务地址 [rootserver ~]# vim /etc/chrony.conf 3> 重启服务 [rootserver ~]# systemctl restart chronyd 4>测试 5>…

证件照制作教程:如何使用在线工具制作高质量的证件照

证件照制作&#xff1a;让你的形象更加完美 介绍 证件照是人们在日常生活、工作和学习中必不可少的一项证明身份的重要文件。而证件照的质量好坏不仅会直接影响到证件审核的效率&#xff0c;还会影响到自己形象的好坏。为了让自己的形象更加完美&#xff0c;我们需要制作高质…

公网使用SSH远程连接安卓手机Termux - Android手机服务器

文章目录 1.安装ssh2.安装cpolar内网穿透3.远程ssh连接配置4.公网远程连接5.固定远程连接地址 转载自cpolar极点云的文章&#xff1a;公网SSH远程连接Termux – 电脑使用安卓Termux 「无需公网IP」 使用安卓机跑东西的时候&#xff0c;屏幕太小&#xff0c;有时候操作不习惯。不…

Python期末复习题库(下)——“Python”

小雅兰期末加油冲冲冲&#xff01;&#xff01;&#xff01; 1. (单选题)下列关于文件打开模式的说法,错误的是( C )。 A. r代表以只读方式打开文件 B. w代表以只写方式打开文件 C. a代表以二进制形式打开文件 D. 模式中使用时,文件可读可写 2. (单选题)下列选项中,以追加…

【操作系统】04.设备管理

IO软件层次 IO设备 机械部件 分类 设备控制器 寄存器编址 IO控制方式 程序直接控制方式 中断驱动方式 DMA方式 通道控制方式 中断处理程序 设备驱动程序 设备独立性软件 IO调度 设备保护 设备分配与回收 静态分配和动态分配 数据结构 设备分配步骤 改进 缓冲区管理 缓冲区的…

5款冷门小工具,让你的电脑变得与众不同

每个人的电脑中都会安装很多软件&#xff0c;可能还保留着很多不为人知的冷门软件。不过虽然冷门&#xff0c;但绝不意味着低能&#xff0c;相反很多冷门软件的功能十分出色。闲话少说&#xff0c;接下来我就给大家推荐5款冷门小工具&#xff0c;看一看有没有你喜欢的。 1.图标…

供应商评估时要考虑的 5 个关键因素

在任何供应商评估中&#xff0c;无论是新供应商还是现有供应商&#xff0c;衡量其生产能力、业绩、风险、质量和环境影响都很重要。然而&#xff0c;如果没有一个有组织的评估方法和一个较大的框架来使评估信息为你所用&#xff0c;单靠衡量这些因素将无济于事。 为什么要进行…

跨部门沟通:这8条方法和建议,让跨部门协作不再难搞!

想要在公司里做出成绩&#xff0c;跨部门沟通是至关重要的。但是&#xff0c;很多人都会遭遇跨部门协作困难的问题。别担心&#xff0c;我们来给你解决方案&#xff01; 1.保持开放和透明 让你的队友们知道你的想法和工作计划&#xff0c;让他们能够对你提出反馈意见。 2.要…

点成分享丨ELISA实验的类型及原理

ELISA实验&#xff0c;即酶联免疫吸附测定&#xff08;Enzyme-Linked Immunosorbent Assay&#xff09;实验&#xff0c;是免疫学中的经典实验之一&#xff0c;它是一种利用抗原抗体特异性结合进行免疫反应的定性和定量检测方法&#xff0c;目前已被广泛应用于生物学、医学、植…

ai语音机器人接听自动外呼配置

一&#xff0c;添加能转接到机器人的拨号方案 {cti_robot_flow_exists({destination_number})} 这是判断路由条件设置的机器人话术是否存在 cti_robot ${destination_number} 启动机器人流程 set park_timeout3600 设置park最大的时间&#xff0c;机器人和用户最大的通话时间…

ChatGPT智能聊天系统重磅升级!新增分销功能!快来体验吧!

ChatGPT智能聊天系统更新至v1.1.2版本啦~主要更新内容如下&#xff1a; 新增功能 注册账号 生成对话海报 分销功能 支持小程序 会员功能控制 敏感词库 用户支持加入黑名单 后台-用户列表新增用户开通会员的筛选 细节优化 对话内容markdown渲染 用户条数用完&#xf…

性能测评:2核2G4M腾讯云轻量应用服务器CPU内存带宽系统盘

腾讯云轻量2核2G4M服务器配置三年396元&#xff0c;一年112元&#xff0c;一定要选择三年&#xff0c;因为续费比较贵&#xff0c;腾讯云轻量应用服务器具有100%CPU性能&#xff0c;系统盘为50GB SSD盘&#xff0c;4M带宽下载速度512KB/秒&#xff0c;月流量300GB&#xff0c;折…

Spring:什么是Spring框架?①

一、Spring简介由来 技术创新就是为了简化技术开发&#xff0c;无数大师耗费心力目的只有一个&#xff0c;如何让开发更为简洁&#xff1f; Spring应运而生&#xff0c;Spring&#xff1a;泉水&#xff0c;春天。意味让技术开发如春天的泉水那般流畅自然。 二、Spring是什么&am…

Python 华为面试手撕代码 + 八股文,机器学习参数调节,损失函数,激活函数,线程、进程和协程

一、手撕代码&#xff1a;力扣原题905 """ 给定一个非负整数数组 A&#xff0c;返回一个由 A 的所有偶数元素组成的数组&#xff0c;后面跟 A 的所有奇数元素。 基础版&#xff1a;你可以返回满足此条件的任何数组作为答案。 进阶版&#xff1a;要求在当前数组…

Revit问题:创建牛腿柱和快速生成圈梁

一、Revit中如何用体量创建牛腿柱 牛腿&#xff1a;悬臂体系的挂梁与悬臂间必然出现搁置构造&#xff0c;通常就将悬臂端和挂梁端的局部构造&#xff0c;又称梁托。牛腿的作用是衔接悬臂梁与挂梁&#xff0c; 并传递来自挂梁的荷载。牛腿柱可以用于桥梁、厂房的搭建&#xff0c…

C Primer Plus第十三章编程练习答案

学完C语言之后&#xff0c;我就去阅读《C Primer Plus》这本经典的C语言书籍&#xff0c;对每一章的编程练习题都做了相关的解答&#xff0c;仅仅代表着我个人的解答思路&#xff0c;如有错误&#xff0c;请各位大佬帮忙点出&#xff01; 由于使用的是命令行参数常用于linux系…

机器学习-5 朴素贝叶斯算法

朴素贝叶斯算法 算法概述数理统计学处理的信息古典学派和贝叶斯学派的争论贝叶斯定理朴素贝叶斯分类训练朴素贝叶斯&#xff1a;朴素假设案例&#xff1a;预测打网球拉普拉斯平滑技术小结 算法流程与步骤算法应用sklearn中的朴素贝叶斯朴素贝叶斯的使用算法实例 算法概述 数理…

年营收超1700亿元 中国机器人行业走向更多场景

文 | BFT机器人 中国已经连续九年成为全球最大的工业机器人应用国&#xff0c;同时还是全球机器人第一生产大国&#xff0c;2022年机器人全行业营收超过1700亿元(人民币&#xff0c;下同)。随着《“机器人”应用行动实施方案》的落地&#xff0c;机器人应用的深度和广度加速扩…