Android - 引用计数(sp、wp、Refbase)

深入了解 Google Gemini:主要功能是什么以及我们为什么要使用它? Google Gemini开启AI新纪元,以其独特的多模式功能重塑技术未来。 阅读详情

Boost和Loki是应用比较广泛的引用计数方案,Android提供了另外一个引用计数方案,就是sp、wp和Refbase组合。

强引用和弱引用区别

一般都是用强引用,如果出现互相引用,至少一方使用弱引用,强弱引用分析 请参考http://blog.youkuaiyun.com/gykimo/article/details/8728735


sp如何管理强引用计数

sp指的是对象的强引用,一般定义如下,sp<Camera> mCamera,那么,mCamera对应的实际对象就交由sp进行管理了,当引用计数为0时,该实际对象就会被销毁。那么在什么情况要增加引用计数,在什么情况要减少引用计数呢?

以下是增加引用计数的情况:

1. 指针赋值拷贝

如:

sp<Camera> mCamera = new camera();

这是一个用得非常多的情况,当new一个对象时,就将该对象的指针交由sp进行管理了,那么这个时候sp就应该增加这个new对象的引用计数,当然这个时候应该是1;

Camera* c = new camera();

sp<Camera> mCamera = c;

该情况是将一个已经new好了的对象指针赋值给mCamera,那么这个时候sp就应该增加这个new对象的引用计数。

sp相应代码如下:

template<typename T>
sp<T>& sp<T>::operator = (T* other)
{
    if (other) other->incStrong(this);
    if (m_ptr) m_ptr->decStrong(this);
    m_ptr = other;
    return *this;
}
其中m_ptr是对象的指针,该代码的逻辑如下

# 如果被复制的对象存在,即other不为NULL,那么就将该对象的引用计数加1;

# 如果原来m_ptr已经指向了某个对象,那么就将某个对象的引用计数减1;

# 然后将other赋值给m_ptr,从此,m_ptr就指向了刚刚设置的对象;


2. 引用赋值拷贝
如:

sp<Camera> c = new camera();

sp<Camera> mCamera = c;

将一个对象c赋值给mCamera,sp对应代码如下:

template<typename T>
sp<T>& sp<T>::operator = (const sp<T>& other) {
    T* otherPtr(other.m_ptr);
    if (otherPtr) otherPtr->incStrong(this);
    if (m_ptr) m_ptr->decStrong(this);
    m_ptr = otherPtr;
    return *this;
}

# 将被赋值对象other的对象引用计数加1;

# 将sp对应对象的引用计数减1;

# 将sp的对象改为other对象;


3. 指针构造拷贝

如:

sp<Camera> mCamera(new camera());

sp相应代码如下:

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

# 将被赋值对象other的对象指针m_ptr赋值给当前sp的m_ptr;

# 如果被赋值对象other的对象存在,则将该对象的引用计数加1;


4. 引用构造拷贝 
如:

sp<Camera> c = new camera();

sp<Camera> mCamera(c);

sp相应代码如下:

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

# 将被赋值对象other的对象指针m_ptr赋值给当前sp的m_ptr;

# 如果被赋值对象other的对象存在,则将该对象的引用计数加1;


以下是减少引用计数的情况:

1. sp析构
如:

{

sp<Camera> c = new camera();

/* do something*/

}

由于c是局部变量,那么,在大括号结束前,c将被析构。

sp对应代码如下:

template<typename T>
sp<T>::~sp()
{
    if (m_ptr) m_ptr->decStrong(this);
}
# 析构并不代表对象真的销毁,因为可能将该对象赋值给了其他sp管理,所以,析构函数只是将对象的引用计数减1;


2. 指针赋值拷贝和引用赋值拷贝

在上面的增加引用计数已经介绍,重新设置sp管理的对象,必须将现在管理的对象的引用计数减1


3. 清除

如:

sp<Camera> mCamera = new camera();

mCamera.clear();

意思设置mCamera不再管理任何对象。

sp代码如下:

template<typename T>
void sp<T>::clear()
{
    if (m_ptr) {
        m_ptr->decStrong(this);
        m_ptr = 0;
    }
}
# 将管理的对象引用计数减1;

# 将m_ptr置空;


wp如何管理弱引用计数

对于wp的处理和sp基本相同,代码部分就不做细致分析了。

我们可以记住,弱引用计数可以通过promote函数升级为强引用。


Refbase机制

我们在上面一节可以看到,sp和wp就是调用Refbase提供的incStrong、decStrong、incWeak和decWeak设置对象的引用计数,那么Refbase是如何实现的呢,以及什么情况下才会将对象销毁呢?

如:

{

sp<Camera> mCamera = new camera();

}

上面如此简单的代码,sp先将camera对象的引用计数加1,然后由于作用域结束,所以mCamera被析构,析构时会将camera对象的引用计数减1,那么这个时候,这个对象的引用计数恰好是0,那么这个时候对象会被销毁吗?我们还是看看Refbase是如何被构造,如何管理引用计数,如何管理对象的。

构造

RefBase::RefBase()
    : mRefs(new weakref_impl(this))
{
//    LOGV("Creating refs %p with RefBase %p\n", mRefs, this);
}
很简单,只是又创建了一个weakref_impl,从这个名字就可以看出它是处理弱引用的类,其实除了弱引用,它还处理强引用。我们还是根据《 心得 - 如何读大型代码和写代码》所描述的方法,我们只需要从宏观上了解weakref_impl的作用,我们现在正在研究Refbase,所以,只要在调用weakref_impl的地方,我们只要能够猜出它完成了什么功能即可,不进行深入分析。

# 创建了weakref_impl对象,并将this指针传递过去,看来weakref_impl对象要通过this指针来回调Refbase的某些方法;

增加强引用计数

mCamera指针赋值拷贝时会增加引用计数,代码如下:

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

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
    const_cast<RefBase*>(this)->onFirstRef();
}
# 增加弱引用,可见,使用强引用时,还伴随着一个弱引用;

# 增加强引用;

# 如果是第一次增加强引用计数,那么就会调用onFirstRef函数,我们在代码经常会看到onFirstRef函数,通过该函数,经常会做些初始化方面的工作;

减少强引用计数

当mCamera析构时就会减少强引用计数,代码如下:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = android_atomic_dec(&refs->mStrong);
#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) {
        const_cast<RefBase*>(this)->onLastStrongRef(id);
        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
            delete this;
        }
    }
    refs->removeWeakRef(id);
    refs->decWeak(id);
}

# 减小强引用计数;

# 销毁对象,如果减小强引用计数之前,强引用计数是1,也就是减小后,强引用计数是0,那么这个时候就delete this了;

# 减小弱引用计数,看来弱引用计数和对象实际存不存在没有必然的关系;


weakref_type功能

我们通过Refbase看出,weakref_type实际管理着强引用和弱引用两个计数。


构造

    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
    {
    }
# 初始化,mStrong(强引用计数)、mWeak(弱引用计数)、mBase(管理的Refbase)

另外,和构造函数一起的还有

    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) { }

它们什么都没有做。


增加弱引用计数

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);
    LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
# 通过原子操作增加mWeak; 


减少弱引用计数

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);
    LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;
    
    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
        if (impl->mStrong == INITIAL_STRONG_VALUE)
            delete impl->mBase;
        else {
//            LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
            delete impl;
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
            delete impl->mBase;
        }
    }
}
# 通过原子操作减少弱引用计数

# 当弱引用计数为0时,那么就会根据某种条件delete this或者delete Refbase

我们在减少强引用和弱引用,看到了OBJECT_LIFETIME_FOREVER和OBJECT_LIFETIME_WEAK两个标志位,那么他们是干什么的呢。

OBJECT_LIFETIME_FOREVER可能代码里面没有设置为这个标识符的类,而OBJECT_LIFETIME_WEAK只有Binder设置了,我们从名字上看出,OBJECT_LIFETIME_FOREVER指的是对象的生命是永远,而OBJECT_LIFETIME_WEAK可能是生命时间按照弱引用计数来决定(一般都是通过强引用计数来决定)。

由于用得很少,所以,这两个标志位对我们的应用而言没有多大关系,但是,我们还是看看代码怎么特殊处理这两个标志位。

在decStrong函数中,

        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
            delete this;
        }

意思是当标志位不是OBJECT_LIFETIME_WEAK时,才会销毁对象。也就是如果标志位是OBJECT_LIFETIME_WEAK,那么如果强引用为0时,也不会销毁对象,看来销毁对象的任务交给其他地方执行了。

还是看上面的decWeak函数,当标志位是OBJECT_LIFETIME_WEAK时,只有当标志位不是OBJECT_LIFETIME_FOREVER才会销毁对象;当标志不是OBJECT_LIFETIME_WEAK时,如果强引用计数也为0,那么就将对象销毁,否则,将weakref_impl销毁。

基于STM32开发板的USB MIDI键盘实现 在本文中,我们介绍了如何使用STM32开发板和相应的软件工具来实现USB MIDI键盘。通过配置STM32CubeMX和编写相应的源代码,我们能够将STM32开发板转换为一个功能齐全的USB MIDI设备。我们将使用STM32开发板和相应的软件工具来实现这一功能,并提供相应的源代码供参考。此时,您应该可以使用STM32开发板作为USB MIDI键盘,并在按键按下时听到相应的音符。在STM32CubeMX中,选择适当的STM32型号,并启动一个新的项目。然后,将生成的可执行文件烧录到STM32开发板中。 阅读详情

相关推荐

别再到处找教程了!Windows下用FFmpeg+Mediamtx+VLC搭建本地RTSP流媒体服务器,保姆级配置流程

本文详细介绍了在Windows系统下使用FFmpeg、Mediamtx和VLC搭建本地RTSP流媒体服务器的完整流程。从环境准备到核心配置,再到推流和拉流的实战技巧,提供了保姆级的避坑指南和优化建议,帮助开发者快速实现稳定的流媒体服务部署。

weixin_42614269的博客 212

Android 智能指针详解 -- RefBase

源码基于:Android N 相关博文:Android 智能指针详解Android 智能指针详解 -- wp Android 智能指针详解 -- spAndroid 智能指针详解 -- RefBase 关于android 智能指针,前面详细分析了sp 和 wp的source code,但是还有一些疑问,在这一篇中全部解释,因为所有的源头都是来自于这里。更多信息可以看 sp的博文 和 wp的博文source code:system/core/include/utils/RefBase.hsystem/core

私房菜 1万+

K-L展开 (Karhunen-Loeve expansion)

K-L变换

susan_wang1的博客 2万+

安卓系统智能指针:RefBase、sp、wp

在安卓中,RefBase结合sp(强指针)和wp(弱指针),实现了通过引用计数的方法来控制对象的生命周期的机制,解决对象的自动释放的问题。在C++编程中,有两个很让人头痛的问题:一是忘记释放动态申 请的对象从而造成内存泄露(通过new生成的对象,需要调用delete才能释放内存);二是对象在一个地方释放后,又在别的地方被使用,从而引起内存访问错误(多个指针指向同一个对象,其中一个指针delete)。在使用RefBase后,动态申请的内存将会被自动释放(有点类似Java的垃圾回收),不需要再使用delete来

liweixin2018的博客 958

实验室LIMS系统选型指南:从赛默飞到金现代,5大主流方案横评

本文深入分析了实验室LIMS系统选型的5大核心维度,包括架构选择、行业适配性、合规性设计、智能化程度和成本模型。通过对比赛默飞、金现代等主流厂商的优劣势,提供实战避坑策略,帮助实验室管理者做出明智决策,提升运营效率和合规水平。

plum99的博客 259

LVI-SAM实战:如何在Ubuntu20.04上快速复现激光-视觉-惯性里程计系统

本文详细解析了在Ubuntu 20.04环境下部署和优化LVI-SAM激光-视觉-惯性里程计系统的全流程。从环境配置、依赖安装到源码编译和实战调参,提供了完整的解决方案和性能优化策略,帮助开发者快速实现厘米级精度的实时位姿估计。

m3n5b7v8c9x的博客 1047

Android-深入理解常见类

文章目录深入理解常见类RefBase、sp和wp初识影子对象RefBase和它的影子sp上场sp析构的影响总结由弱生强总结破解生死魔咒延长生命的魔咒LIFETIME_WEAK的魔力LIFETIME_FOREVER 深入理解常见类 RefBase、sp和wp RefBase是Android中所有对象的始祖,RefBase结合sp和wp,实现了一套通过引用计数的方式来控制对象生命周期的机制,sp是strong pointer,wp是weak pointer。 初识影子对象 class A : public Re

李木子的博客 3524

ROS 2 + Astra Pro深度相机实战:5分钟搞定远程可视化与距离检测(附避坑指南)

本文提供了一份详尽的ROS 2与Astra Pro深度相机集成实战指南。通过5分钟快速部署,实现远程可视化与实时目标距离检测。文章涵盖驱动安装、SSH+X11远程调试、核心测距节点编写,并附有详细的避坑指南,帮助开发者高效构建机器人视觉感知系统。

weixin_29232507的博客 278

Android源码分析 理解Refbase

最近解决稳定性问题,遇到sp问题,本文就简单梳理RefBase和sp、wp下。

baiiu 943

Disk Drill 使用教程

Disk Drill只需单击几下,即可恢复USB闪存驱动器上已删除的数据,包括丢失的照片,文档和其他文件类型。Disk Drill是真正的多合一应用程序,可在Mac上恢复闪存驱动器。Disk Drill将自动应用最佳的笔式驱动器恢复算法来恢复Mac数据。 USB闪存驱动器是一种传输和存储信息的好方法,但是如果错误地丢失或删除了存储在其中的文件,会发生什么呢?当您需要Mac的闪存驱动器数据恢复软件时,Disk Drill是您的最佳选择。Disk Drill将扫描您的USB闪存驱动器(也称为笔式驱动器)并查找

zjj778899的博客 1万+

CAN 通信测试工具 canutils

概述 CAN,Controller Area Network,控制器局域网,一种高可靠性的现场总线。广泛用于汽车电子、工业控制通信。 canutils 工具包内含 5 个独立的程序:canconfig、candump、canecho、cansend、cansequence。这几个程序的功能简述如下: canconfig:用于配置 CAN 总线接口的参数,主要是波特率和模式。 candump...

一个软件工程师的技术笔记 1万+

Android Framework学习(六)之RefBase,SP,WP

Android中通过引用计数来实现智能指针,并且实现有强指针与弱指针。由对象本身来提供引用计数器,但是对象不会去维护引用计数器的值,而是由智能指针来管理。要达到所有对象都可用引用计数器实现智能指针管理的目标,可以定义一个公共类,提供引用计数的方法,所有对象都去继承这个公共类,这样就可以实现所有对象都可以用引用计数来管理的目标,在Android中,这个公共类就是RefBase,同时还有一个简单版本LightRefBase。

Android23333的博客 466

电机驱动器MP6540H应用设计

/ ... 其他初始化函数(SystemClock_Config, MX_GPIO_Init, MX_TIM1_Init, MX_TIM2_Init)需要你根据STM32CubeMX生成或手动编写。// 假设霍尔传感器引脚是 GPIO_PIN_8, GPIO_PIN_9, GPIO_PIN_10。// 低侧U相GPIO引脚。

zhusl6688的博客 660

安卓智能指针sp、wp、RefBase浅析

安卓智能指针sp、wp、RefBase浅析

似霰的博客 1779

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Android 上C++对象的自动回收机制分析

前面说过,智能指针管理着对象的引用计数,Android中智能指针的实现是sp和wp。假设A对象是由MA模块来创建和销毁的,MB模块的B对象增加A对象的引用计数来使用A对象,有一种情况 是:MA模块比MB模块提前被销毁,由于B对象使用着A,A的引用计数不为0,则A不会被销毁。使用对象的方式是通过智能指针引用该对象,智能指针也是C++对象,利用C++的构造析构自动调用的特性,在构造函数中将对象的引用计数加1,析构函数中减1,当计数减为0时delete该对象,这样通过智能指针+引用计数就实现了对象的自动化管理。

2501_90390086的博客 1629

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Android中的RefBase,sp和wp

在Android中,RefBase结合了sp和wp,实现了一套通过引用计数的方法来控制对象声明周期的方法。 RefBase的定义在/frameworks/base/include/utils/RefBase.h,实现在/frameworks/base/libs/utils/RefBase.cpp。 wp的定义在/frameworks/base/include/utils/RefBase.h

人在码途 5910

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

五分钟搞懂Android智能指针SP WP

问题 不像java这种runtime提供内存回收机制的语言,c c++开发中经常困扰开发者的是变量的分配与回收,当new完对象而忘记delete就会造成内存泄漏,如果delete了还在别处引用当对象,就会形成野指针 解决 一种内存回收策略叫引用计数,当对象被引用时,引用计数就+1,不再引用的时候引用计数就-1,当引用计数为0时,就回收对象。这种内存回收机制的问题是不能回收循环引用的对象,a对象有b...

congqingbin的专栏 1285

Android framework RefBase,sp,wp

Android framework RefBase,sp,wp 的使用与源码分析

weixin_45767368的博客 1201

Android sp和wp指针

经常会在android的framework代码中发现sp和wp这样的指针,平时看的时候都把他当成一个普通的指针封装过掉了,这几天终于忍不住了,想深入了解一下。 相关的代码: frameworks/base/include/utils/RefBase.h frameworks/base/libs/utils/RefBase.cpp sp和wp都是一个模板类,看一下sp类的定义: templ

hgl868的专栏 990

Android 智能指针(3):终篇

Android 智能指针从设计上便避免了多个智能指针管理同一个原始指针的风险。最后需要再次强调,RefBase乃是Android 指针的灵魂

yeguanxuri的博客 600

【C++】Android (Light)RefBase-sp-wp引用计数-智能指针源码分析

1、RefBase简介什么是RefBase?RefBase是Android中的一个C++类,用于内存管理,相关的类还包括LightRefBase、sp和wp。在Android源码中的许多地方都可以看到RefBase的使用,那么,为什么RefBase这么受欢迎呢?这源于C++的指针与动态内存管理,指针本身就是一个复杂的概念,而动态内存管理又提高了其复杂性,new一般情况下与delete配对使用,防止内

Making Life Better. Making Others Better. 3808

android系统核心机制 基础(01)智能指针wp & sp

系列文章解读&说明: Android系统核心机制基础 的 分析主要分为以下部分: android系统核心机制 基础(01)智能指针wp & sp android系统核心机制 基础(02)Thread类解析 android系统核心机制 基础(03)Thread 同步机制 android系统核心机制 基础(04)handler message机制 java android系...

wangdsh的博客 1590

Android指针管理:RefBase,SP,WP

Android中通过引用计数来实现智能指针,并且实现有强指针与弱指针。由对象本身来提供引用计数器,但是对象不会去维护引用计数器的值,而是由智能指针来管理。         要达到所有对象都可用引用计数器实现智能指针管理的目标,可以定义一个公共类,提供引用计数的方法,所有对象都去继承这个公共类,这样就可以实现所有对象都可以用引用计数来管理的目标,在Android中,这个公共类就是RefBase,同

AngelDevil的专栏 650

Android Framework学习之RefBase,SP,WP解析

LightRefBase仅仅提供了引用计数的方法,具体引用数应该怎么管理,就要通过智能指针类来管理了,每当有一个智能指针指向对象时,对象的引用计数要加1,当一个智能指针取消指向对象时,对象的引用计数要减1,在C++中,当一个对象生成和销毁时会自动调用(拷贝)构造函数和析构函数,所以,对对象引用数的管理就可以放到智能指针的(拷贝)构造函数和析构函数中。sp 管理一个对象指针时,对象的强、弱引用数同时加1,sp销毁时,对象的强、弱引用数同时减1。

FeiPeng_的博客 1013

Android中的sp和wp指针

经常会在android的framework代码中发现sp和wp这样的指针,平时看的时候都把他当成一个普通的指针封装过掉了,这几天终于忍不住了,想深入了解一下。相关的代码:frameworks/base/include/utils/RefBase.hframeworks/base/libs/utils/RefBase.cppsp和wp都是一个模板类,看一下sp类的定义:template class sp { public: typedef typename RefBase::weakref_

DroidPhone的专栏 3万+

android RefBase、sp、wp

首先RefBase在android的c++部分是作为一个所有类的基类,其作用跟Java中的Object类似 这个类中存在一个私有成员: weakref_impl* const mRefs;(weakref_impl是weakref_type的子类) 这个mRefs是“影子对象”,是管理一个对象的引用计数的关键 RefBase的构造中mRefs(new weakref_impl(this))...

weixin_34314962的博客 177
上一篇: boost - 智能指针介绍
下一篇: 设计模式 - 简单工厂、工厂方法、抽象工厂
G机器猫
博客等级 码龄16年 106粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值