sp与wp智能指针
sp<> 和 wp<> 是 Android RefBase 的两个句柄层:sp 表示强所有权,wp 只保存对象指针和控制块,必须通过 promote() 才能尝试取得强引用。它们的复制、移动、赋值和析构都直接调用 incStrong/decStrong 或 incWeak/decWeak;所以“把裸指针包进 sp”不是普通指针转换,而是一次所有权变更。
本文面向已经读过 RefBase引用计数、BpBinder代理对象 和 BBinder本地对象 的读者。本文专门讲模板句柄如何消费 RefBase,不重复控制块算法,也不把 sp/wp 与 std::shared_ptr 的语义混为一谈。
1. 强句柄
1.1 构造策略
源码文件:system/core/libutils/include/utils/StrongPointer.h
template <typename... Args>
static inline sp<T> make(Args&&... args);
sp(const sp<T>& other);
sp(sp<T>&& other) noexcept;
~sp();源码推荐 sp<T>::make(),不推荐 sp<T> p = new T 的隐式构造,因为裸指针可能已经由其他 owner 管理,形成 double ownership。
1.2 复制移动
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;
}复制增加强引用;移动只转移指针并把源置空,不增加计数。
1.3 清理
template<typename T>
sp<T>::~sp() {
if (m_ptr) m_ptr->decStrong(this);
}
template<typename T>
void sp<T>::clear() {
T* oldPtr = m_ptr;
if (oldPtr) {
oldPtr->decStrong(this);
m_ptr = nullptr;
}
}析构和 clear() 都可能触发对象析构以及 Binder 的最后 strong 回调。
2. 弱句柄
2.1 构造复制
源码文件:system/core/libutils/binder/include/utils/RefBase.h
template<typename T>
wp<T>::wp(const sp<T>& other)
: m_ptr(other.m_ptr) {
m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;
}
template<typename T>
wp<T>::wp(const wp<T>& other)
: m_ptr(other.m_ptr), m_refs(other.m_refs) {
if (m_ptr) m_refs->incWeak(this);
}从 sp 构造 wp 会创建弱引用;复制 wp 只增加控制块弱计数。
2.2 清理
template<typename T>
wp<T>::~wp() {
if (m_ptr) m_refs->decWeak(this);
}
template<typename T>
void wp<T>::clear() {
if (m_ptr) {
m_refs->decWeak(this);
m_refs = nullptr;
m_ptr = nullptr;
}
}wp 清理不直接删除对象,只减少弱引用。
2.3 比较语义
空 sp/wp 的指针和控制块都为空;弱指针比较使用控制块身份,避免旧对象销毁后同一地址复用造成错误相等。
3. 弱引用提升
3.1 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;
}promote() 返回新的 sp 或空值;调用者只有在返回非空时才能访问对象。ProcessState 的 handle 表则通过 attemptIncWeak() 判断代理控制块是否仍可复用。
3.2 fromExisting
sp<T>::fromExisting() 和 wp<T>::fromExisting() 只用于已知已有引用的内部场景;后者没有现存弱引用会 fatal,不是普通业务构造器。
4. 赋值与线程
4.1 sp赋值
template<typename T>
sp<T>& sp<T>::operator=(const sp<T>& other) {
T* oldPtr = m_ptr;
T* otherPtr = other.m_ptr;
if (otherPtr) otherPtr->incStrong(this);
if (oldPtr) oldPtr->decStrong(this);
m_ptr = otherPtr;
return *this;
}先增加新对象再减少旧对象,避免自赋值时短暂归零。
4.2 线程安全
不同 sp<>/wp<> 实例可并发指向同一对象;同一个实例存在写入时不具备线程安全。引用计数原子化不保护 m_ptr、业务字段或析构后的访问。
5. 测试边界
5.1 移动与弱复制
源码文件:system/core/libutils/binder/RefBase_test.cpp
StrongMoves 断言移动 sp 不增加强计数且源句柄置空;WeakCopies 断言复制 wp 增减弱计数,并验证清理边界。
5.2 提升与地址复用
NoStrongCountPromoteFromWeak 验证弱引用提升;ReplacedComparison 让旧对象销毁后在同一地址创建新对象,断言旧 wp 不等于新对象。
5.3 可执行阅读
rg -n "class sp|sp<T>::sp|sp<T>::~sp|sp<T>::operator=|sp<T>::clear" \
system/core/libutils/include/utils/StrongPointer.h
rg -n "class wp|wp<T>::wp|wp<T>::~wp|wp<T>::promote|wp<T>::clear" \
system/core/libutils/binder/include/utils/RefBase.h
rg -n "StrongMoves|WeakCopies|NoStrongCountPromoteFromWeak|ReplacedComparison" \
system/core/libutils/binder/RefBase_test.cpp排查对象提前释放时,先看是 sp 强引用归零还是 wp 控制块归零,再确认是否错误使用裸指针、release() 或 fromExisting();安全访问弱对象应调用 promote(),不要使用 unsafe_get() 解引用。
