智能指针简介
在使用C++来编写代码的过程中,经常出现内存泄漏和野指针问题。为了避免出现上述问题,一般的做法就是使用引用计数的方法:
每当有一个指针指向了一个new出来的对象时,就对这个对象的引用计数增加1,每当有一个指针不再使用这个对象时,就对这个对象的引用计数减少1,每次减1之后,如果发现引用计数值为0时,那么,就要delete这个对象了,这样就避免了忘记delete对象或者这个对象被delete之后其它地方还在使用的问题了。
但是,如何实现这个对象的引用计数呢?肯定不是由开发人员来手动地维护了,易出错。这时候,智能指针就粉墨登场了。首先,智能指针是一个对象,不过这个对象代表的是另外一个真实使用的对象,当智能指针指向实际对象的时候,就是智能指针对象创建的时候,当智能指针不再指向实际对象的时候,就是智能指针对象销毁的时候,我们知道,在C++中,对象的创建和销毁时会分别自动地调用对象的构造函数和析构函数,这样,负责对真实对象的引用计数加1和减1的工作就落实到智能指针对象的构造函数和析构函数的身上了,这也是为什么称这个指针对象为智能指针的原因。(一下子可能很难理解,跳过,看完例子再回来理解这段话!!)
在计算机科学领域中,提供垃圾收集(Garbage Collection)功能的系统框架,即提供对象托管功能的系统框架,例如Java应用程序框架,也是采用上述的引用计数技术方案来实现的,然而,简单的引用计数技术不能处理系统中对象间循环引用的情况。
考虑这样的一个场景,系统中有两个对象A和B,在对象A的内部引用了对象B,而在对象B的内部也引用了对象A。当两个对象A和B都不再使用时,垃圾收集系统会发现无法回收这两个对象的所占据的内存的,因为系统一次只能收集一个对象,而无论系统决定要收回对象A还是要收回对象B时,都会发现这个对象被其它的对象所引用,因而就都回收不了,这样就造成了内存泄漏。这样,就要采取另外的一种引用计数技术了,即对象的引用计数同时存在强引用和弱引用两种计数,当父对象要引用子对象时,就对子对象使用强引用计数技术,而当子对象要引用父对象时,就对父对象使用弱引用计数技术,而当垃圾收集系统执行对象回收工作时,只要发现对象的强引用计数为0,而不管它的弱引用计数是否为0,都可以回收这个对象,但是,如果我们只对一个对象持有弱引用计数,当我们要使用这个对象时,就不直接使用了,必须要把这个弱引用升级成为强引用时,才能使用这个对象,在转换的过程中,如果对象已经不存在,那么转换就失败了,这时候就说明这个对象已经被销毁了,不能再使用了。
Android中的智能指针
说到智能指针,熟悉c++的朋友知道,c++默认提供了一套默认指针的实现,auto_ptr, shared_ptr, weak_ptr, unique_ptr。其中auto_ptr是c++中最早的智能指针实现,但是具有很大的局限性,所以在c++11中启用了auto_ptr,而改用后面三种智能指针。
讲道理,c++11提供的智能指针很优秀,并不比android中实现的智能指针方案差,那么为什么android要自己实现一套智能指针呢?纯属个人分析,在android早起版本时c++11规范并没有出来,但是开发者不满意auto_ptr表现,所以自己实现了一套智能指针。等到c++11规范出来的时候,android已经成型,太多地方用到了智能指针,所以修改复杂,于是就不改了……猜测不知道对错,但是有一点肯定的是,绝对不是android底层不能使用c++11,因为最新版(android 8)的android智能指针实现代码里使用了c++11的内容。
回到主题Android的智能指针:
Android系统提供了强大的智能指针技术供我们使用,这些智能指针实现方案既包括简单的引用计数技术,也包括了复杂的引用计数技术,即对象既有强引用计数,也有弱引用计数,对应地,这三种智能指针分别就称为轻量级指针(Light Pointer)、强指针(Strong Pointer)和弱指针(Weak Pointer)。无论是轻量级指针,还是强指针和弱指针,它们的实现框架都是一致的,即由对象本身来提供引用计数器,但是它不会去维护这个引用计数器的值,而是由智能指针来维护,就好比是对象提供素材,但是具体怎么去使用这些素材,就交给智能指针来处理了。由于不管是什么类型的对象,它都需要提供引用计数器这个素材,在C++中,我们就可以把这个引用计数器素材定义为一个公共类,这个类只有一个成员变量,那就是引用计数成员变量,其它提供智能指针引用的对象,都必须从这个公共类继承下来,这样,这些不同的对象就天然地提供了引用计数器给智能指针使用了。总的来说就是我们在实现智能指会的过程中,第一是要定义一个负责提供引用计数器的公共类,第二是我们要实现相应的智能指针对象类,后面我们会看到这种方案是怎么样实现的。
轻量级指针 LightRefBase
看罗老师的介绍,LightRefBase定义在frameworks/base/include/utils/RefBase.h文件中,但是我在看android8.0的源码时,LightRefBase的定义已经有些差别了/system/core/include/utils/LightRefBase.h
templateclass LightRefBase{public: inline LightRefBase() : mCount(0) { } inline void incStrong(__attribute__((unused)) const void* id) const { mCount.fetch_add(1, std::memory_order_relaxed); } inline void decStrong(__attribute__((unused)) const void* id) const { if (mCount.fetch_sub(1, std::memory_order_release) == 1) { std::atomic_thread_fence(std::memory_order_acquire); delete static_cast (this); } } //! DEBUGGING ONLY: Get current strong ref count. inline int32_t getStrongCount() const { return mCount.load(std::memory_order_relaxed); } typedef LightRefBase basetype;protected: inline ~LightRefBase() { }private: friend class ReferenceMover; inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { } inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }private: mutable std::atomic mCount;};
首先它是一个模板类,拥有一个atomic<int32_t> 类型的计数器(atomic是一个原子操作类,在c++11中新定义的。)提供了desString 和 incString的计数器增减方案,还有一个getStringCount的获取计数器数量的方法。
这个类的实现方法可能和罗老师介绍的android2.3版本有些区别,主要集中在对于mCount的原子操作上,但是实际上这并不影响我们对于智能指针的理解和使用。
在实现上,需要关注的是decStrong方法,如果强引用全部删除之后,我们会delete本对象!
好了,上面我们定义了引用计数器(就是需要使用智能指针的对象都需要继承的那个公共类),它本身并没有智能指针的功能,我们需要使用一个类来控制这个引用计数,这个类就是sp类,它不仅仅是LightRefBase计数器的智能指针,同时也是强指针引用计数器的智能指针。
templateclass sp {public: inline sp() : m_ptr(0) { } sp(T* other); // NOLINT(implicit) sp(const sp & other); sp(sp && other); template sp(U* other); // NOLINT(implicit) template sp(const sp & other); // NOLINT(implicit) template sp(sp && other); // NOLINT(implicit) ~sp(); // Assignment sp& operator = (T* other); sp& operator = (const sp & other); sp& operator = (sp && other); template sp& operator = (const sp & other); template sp& operator = (sp && other); template sp& operator = (U* other); //! Special optimization for use by ProcessState (and nobody else). void force_set(T* other); // Reset void clear(); // Accessors inline T& operator* () const { return *m_ptr; } inline T* operator-> () const { return m_ptr; } inline T* get() const { return m_ptr; } // Operators COMPARE(==) COMPARE(!=) COMPARE(>) COMPARE(<) COMPARE(<=) COMPARE(>=)private: template friend class sp; template friend class wp; void set_pointer(T* ptr); T* m_ptr;};
如果你是java用户,对c++不太熟悉的话解读代码可能有些困难,但是相信我,c/c++你值得接触。
首先在template模板类型中,对于模板并没有类型检测,所以你会看到下面这样的代码
templatesp ::sp(T* other) : m_ptr(other) { if (other) other->incStrong(this);}template sp ::sp(const sp & other) : m_ptr(other.m_ptr) { if (m_ptr) m_ptr->incStrong(this);}
我根本不知道m_ptr的具体类型(它的类型是T,一个模板),但是我们可以直接调用 incStrong这样的方法,编译器不会帮我们检测是否合法,一切自在开发者心中。
具体的实现代码可以自己看一下文件,无非是构造的时候调用incString,销毁的时候调用decStrong方法。至于为什么有那么多的重载,还有赋值运算符的重载,这就涉及到c++的拷贝构造等内容了。
总之sp的功能就是模板包装LightRefBase对象,控制它的引用计数。
#include#include using namespace android;class LightClass : public LightRefBase {public: LightClass() { printf("Construct LightClass Object."); } virtual ~LightClass() { printf("Destory LightClass Object."); }};int main(int argc, char** argv){ LightClass* pLightClass = new LightClass(); sp lpOut = pLightClass; printf("Light Ref Count: %d.\n", pLightClass->getStrongCount()); //1 { sp lpInner = lpOut; printf("Light Ref Count: %d.\n", pLightClass->getStrongCount()); //2 } printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());//1 return 0;}//print://Construct LightClass Object. //Light Ref Count: 1. //Light Ref Count: 2. //Light Ref Count: 1. //Destory LightClass Object.
例子也直接引用了罗老师的(PS:本机没有编译源代码,所以没办法自己写例子啊)。
首先我们定义了一个最简单的类,但是需要继承与LightRefBase<T>类。
在使用的时候我们首先创建了一个对象(创建的时候还没有开始引用计数),然后赋值给一个只能指针(引用计数开始了)。然后在局部作用域中,我们再次定义一个智能指针,并且指向了原来的对象,所以引用计数再次+1,当除了局部作用域,智能指针析构,所以引用计数-1。最终退出方法,lpout也会被析构,引用计数继续-1,这个时候引用计数为0,对象LightClass就会被delete(我们没有调用delete方法释放指针,但是它确实被自动释放了,这就是智能指针的作用)。
强指针
强指针应用计数类的定义在/system/core/include/utils/RefBase.h中
class RefBase{public: void incStrong(const void* id) const; void decStrong(const void* id) const; void forceIncStrong(const void* id) const; //! DEBUGGING ONLY: Get current strong ref count. int32_t getStrongCount() const; class weakref_type { public: RefBase* refBase() const; void incWeak(const void* id); void decWeak(const void* id); // acquires a strong reference if there is already one. bool attemptIncStrong(const void* id); // acquires a weak reference if there is already one. // This is not always safe. see ProcessState.cpp and BpBinder.cpp // for proper use. bool attemptIncWeak(const void* id); //! DEBUGGING ONLY: Get current weak ref count. int32_t getWeakCount() const; //! DEBUGGING ONLY: Print references held on object. void printRefs() const; //! DEBUGGING ONLY: Enable tracking for this object. // enable -- enable/disable tracking // retain -- when tracking is enable, if true, then we save a stack trace // for each reference and dereference; when retain == false, we // match up references and dereferences and keep only the // outstanding ones. void trackMe(bool enable, bool retain); }; weakref_type* createWeak(const void* id) const; weakref_type* getWeakRefs() const; //! DEBUGGING ONLY: Print references held on object. inline void printRefs() const { getWeakRefs()->printRefs(); } //! DEBUGGING ONLY: Enable tracking of object. inline void trackMe(bool enable, bool retain) { getWeakRefs()->trackMe(enable, retain); } typedef RefBase basetype;protected: RefBase(); virtual ~RefBase(); //! Flags for extendObjectLifetime() enum { OBJECT_LIFETIME_STRONG = 0x0000, OBJECT_LIFETIME_WEAK = 0x0001, OBJECT_LIFETIME_MASK = 0x0001 }; void extendObjectLifetime(int32_t mode); //! Flags for onIncStrongAttempted() enum { FIRST_INC_STRONG = 0x0001 }; // Invoked after creation of initial strong pointer/reference. virtual void onFirstRef(); // Invoked when either the last strong reference goes away, or we need to undo // the effect of an unnecessary onIncStrongAttempted. virtual void onLastStrongRef(const void* id); // Only called in OBJECT_LIFETIME_WEAK case. Returns true if OK to promote to // strong reference. May have side effects if it returns true. // The first flags argument is always FIRST_INC_STRONG. // TODO: Remove initial flag argument. virtual bool onIncStrongAttempted(uint32_t flags, const void* id); // Invoked in the OBJECT_LIFETIME_WEAK case when the last reference of either // kind goes away. Unused. // TODO: Remove. virtual void onLastWeakRef(const void* id);private: friend class weakref_type; class weakref_impl; RefBase(const RefBase& o); RefBase& operator=(const RefBase& o);private: friend class ReferenceMover; static void renameRefs(size_t n, const ReferenceRenamer& renamer); static void renameRefId(weakref_type* ref, const void* old_id, const void* new_id); static void renameRefId(RefBase* ref, const void* old_id, const void* new_id); weakref_impl* const mRefs;};
同样定义了 incStrong 和decStrong方法,用于增减引用计数(这也是为什么强指针也能使用sp<T>类来控制引用计数的原因)。和轻量级指针引用计数不同的是,我们的引用计数量不再是一个int类型(轻量级中使用std::atomic<int32_t>)来记录引用数量,而是使用了一个weakref_impl指针类型的变量mRefs。
weakref_impl是继承与上面weakref_type的一个类,定义在/system/core/libutils/RefBase.cpp文件中。
class RefBase::weakref_impl : public RefBase::weakref_type{public: std::atomicmStrong; std::atomic mWeak; RefBase* const mBase; std::atomic mFlags;#if !DEBUG_REFS explicit weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE) , mWeak(0) , mBase(base) , mFlags(0) { } void addStrongRef(const void* /*id*/) { } void removeStrongRef(const void* /*id*/) { } void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { } void addWeakRef(const void* /*id*/) { } void removeWeakRef(const void* /*id*/) { } void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { } void printRefs() const { } void trackMe(bool, bool) { }#else......#endif
我们只关注这一段,下面省略的部分是用于debug的,这里直接略过了。在release版本上,这些函数都是空实现,所以实际有用的就是最上面定义的4个成员变量。mStrong 和 mWeak分别用来做强引用和弱引用的计数,mBase指向实现引用计数的对象本身,mFlags是标志位,默认值是0,作用后面一点点看。
我们会以sp<T>的构造函数,关于增减引用计数,使用的方法是incStrong和decStrong。所以RefBase中一定也是通过这两个方法真正来实现关于引用计数的增减的,我们先来看一下incStrong(RefBase.cpp中)
RefBase::RefBase() : mRefs(new weakref_impl(this)){}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();}
这里把构造函数也列出来,我们可以知道,mRefs的对象就是wekref_impl。
incStrong首先调用incWeak看函数名是用来增加弱引用计数的
void RefBase::weakref_type::incWeak(const void* id){ weakref_impl* const impl = static_cast(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);}
弱引用简单+1,没有其他操作。
然后调用addStrongRef,在release版本中是个空实现,所以这里也就不介绍了。
关键是下面 fetch_add 在强引用计数上+1(返回值是原来的值)。如果是初始值(INITIAL_STRONG_VALUE),表示是第一次被强引用,那么久调用fetch_sub,将mStrong归1(初始值不为0,是1<<28,所以上面+1之后是 1<<28+1,这里减去初始值之后才会变成1。相比使用boolean标记为,这样的方式似乎也挺优雅的,值得学习),并且还会调用onFirstRef来做第一次被引用的处理(开发者自己定义,RefBase类中是个空实现)。
然后是decStrong
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 LOG_ALWAYS_FATAL_IF(BAD_STRONG(c), "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; // The destructor does not delete refs in this case. } } // 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);}
removeStrongRef也是空实现。
使用fetch_sub对强引用计数器-1,如果当前没有其他强引用了,那么调用onLastStrongRef方法(和之前的onFirstRef对应)。
在继续解读之前需要了解一个enum
//! Flags for extendObjectLifetime() enum { OBJECT_LIFETIME_STRONG = 0x0000, OBJECT_LIFETIME_WEAK = 0x0001, OBJECT_LIFETIME_MASK = 0x0001 };
这个枚举量是用来控制对象生命周期的,如果设置为OBJECT_LIFETIME_STRONG表示,对象如果强引用计数器为0,就表示生命周期终止,需要delete,如果设置为OBJECT_LIFETIME_WEAK,表示,只有当弱引用和强引用计数器都为0时,才会delete对象。
所以回到上面代码,decStrong会判断对象的生命周期类型,如果是OBJECT_LIFETIME_STRONG,那么我们会直接delete自己。delete会调用析构函数
RefBase::~RefBase(){ int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed); // Life-time of this object is extended to WEAK, in // which case weakref_impl doesn't out-live the object and we // can free it now. if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { // It's possible that the weak count is not 0 if the object // re-acquired a weak reference in its destructor if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) { delete mRefs; } } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) { // We never acquired a strong reference on this object. LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0, "RefBase: Explicit destruction with non-zero weak " "reference count"); // TODO: Always report if we get here. Currently MediaMetadataRetriever // C++ objects are inconsistently managed and sometimes get here. // There may be other cases, but we believe they should all be fixed. delete mRefs; } // For debugging purposes, clear mRefs. Ineffective against outstanding wp's. const_cast(mRefs) = NULL;}
析构函数中做了一件很重要的事情,释放mRefs计数对象(我们在构造函数的时候new的,所以需要确保释放)。但是并不是直接mRefs就可以的,如果当前对象生命周期是受弱引用控制的,那么只有在弱引用计数为0的时候,才会去销毁mRefs(其实如果调用了析构函数,并且是弱引用控制的,那么理论上弱引用计数应该就是0)。如果生命周期受到强引用控制,那么只有在该对象从来没有申请过强引用的情况下才会释放mRefs,如果申请过强引用,这种情况该对象的释放会在decWeak方法中执行(析构时可能弱引用计数还不为0,这个时候这个计数对象是不应该销毁的)。
回到decStrong,最后,不论生命周期如何,还需要调用decWeak将弱引用-1。
void RefBase::weakref_type::decWeak(const void* id){ weakref_impl* const impl = static_cast(this); impl->removeWeakRef(id); const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); LOG_ALWAYS_FATAL_IF(BAD_WEAK(c), "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 // outlives 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) { // Decrementing a weak count to zero when object never had a strong // reference. We assume it acquired a weak reference early, e.g. // in the constructor, and will eventually be properly destroyed, // usually via incrementing and decrementing the strong count. // Thus we no longer do anything here. We log this case, since it // seems to be extremely rare, and should not normally occur. We // used to deallocate mBase here, so this may now indicate a leak. ALOGW("RefBase: Object at %p lost last weak reference " "before it had a strong reference", 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; }}
如果你看过罗老师的智能指针,你回发现,这个方法已经有所不同了。removeWeakRef这些在release中空实现略过。通过fetch_sub在弱引用计数上-1,然后判断是否为最后一个弱引用,如果不是,直接返回。接下去的地方逻辑有些绕,先来看第一个else部分:
如果对象生命周期受到弱引用控制,那么当弱引用为0时,delete impl->mBase(相当于delete this!)。
如果受到强引用控制,那么分成两种情况,是否存在或者曾经存在强引用,如果从没有存在过强引用,并且这个时候弱引用为0了,这种情况一般不会出现,所以什么也不做,就打印。如果存在过强引用,那么对象的生命周期是强引用控制的,理论上在强引用为0的时候会被释放,不需要你来负责释放对象,只需要负责释放mRef对象(这个对象的生命周期并不是和对象本身绑定的,对象地址会被wp<T>持有,即使本对象delete了,mRef还是存在,并且调用decWeak完全没问题,这种情况下就不能再析构函数中直接释放mRef对象,而是需要在弱引用计数为0之后主动释放对象)。
强指针的类就是sp<T>这个前面已经说过了,这里就不介绍了。
弱指针
弱指针使用的引用计数类同样是RefBase类,这是不出所料的,因为在分析强指针的时候已经看到了一些关于弱指针的引用计数情况。
弱指针的实现类是wp<T>,位于/system/core/inlucde/utils/RefBase.h文件中,和RefBase类定义在同一个头文件中。
templateclass wp{public: typedef typename RefBase::weakref_type weakref_type; inline wp() : m_ptr(0) { } wp(T* other); // NOLINT(implicit) wp(const wp & other); explicit wp(const sp & other); template wp(U* other); // NOLINT(implicit) template wp(const sp & other); // NOLINT(implicit) template wp(const wp & other); // NOLINT(implicit) ~wp(); // Assignment wp& operator = (T* other); wp& operator = (const wp & other); wp& operator = (const sp & other); template wp& operator = (U* other); template wp& operator = (const wp & other); template wp& operator = (const sp & other); void set_object_and_refs(T* other, weakref_type* refs); // promotion to sp sp promote() const; // Reset void clear(); // Accessors inline weakref_type* get_refs() const { return m_refs; } inline T* unsafe_get() const { return m_ptr; } // Operators COMPARE_WEAK(==) COMPARE_WEAK(!=) COMPARE_WEAK(>) COMPARE_WEAK(<) COMPARE_WEAK(<=) COMPARE_WEAK(>=) inline bool operator == (const wp & o) const { return (m_ptr == o.m_ptr) && (m_refs == o.m_refs); } template inline bool operator == (const wp & o) const { return m_ptr == o.m_ptr; } inline bool operator > (const wp & o) const { return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); } template inline bool operator > (const wp & o) const { return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); } inline bool operator < (const wp & o) const { return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); } template inline bool operator < (const wp & o) const { return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); } inline bool operator != (const wp & o) const { return m_refs != o.m_refs; } template inline bool operator != (const wp & o) const { return !operator == (o); } inline bool operator <= (const wp & o) const { return !operator > (o); } template inline bool operator <= (const wp & o) const { return !operator > (o); } inline bool operator >= (const wp & o) const { return !operator < (o); } template inline bool operator >= (const wp & o) const { return !operator < (o); }private: template friend class sp; template friend class wp; T* m_ptr; weakref_type* m_refs;};
wp<T>类的定义和sp<T>其实是比较接近的,最大的区别在于wp<T> 不仅保存了 T的指针 m_ptr,而且保存了T中引用计数的指针m_refs。
首先,在最基本型的构造函数中,会调用createWeak方法,并且将返回值存入m_refs中
templatewp ::wp(T* other) : m_ptr(other){ if (other) m_refs = other->createWeak(this);}RefBase::weakref_type* RefBase::createWeak(const void* id) const{ mRefs->incWeak(id); return mRefs;}
而createWeak方法(定义在RefBase.cpp中)一并列在了上面,调用incWeak增加弱引用计数,并且返回mRefs对象指针。
sp的析构函数会调用decStrong,相应的,wp的析构函数应该会调用decWeak。但是不同的地方在于,sp只世界调用对象T*的方法,而decWeak方法定义在weakref_type对象上。
关于decWeak的实现在上面章节已经介绍过了。
分析到这里,弱指针还没介绍完,它最重要的特性我们还没有分析到。前面我们说过,弱指针的最大特点是它不能直接操作目标对象,这是怎么样做到的呢?秘密就在于弱指针类没有重载*和->操作符号,而强指针重载了这两个操作符号。但是,如果我们要操作目标对象,应该怎么办呢,这就要把弱指针升级为强指针了:
templatesp wp ::promote() const{ sp result; if (m_ptr && m_refs->attemptIncStrong(&result)) { result.set_pointer(m_ptr); } return result;}
其中attemptIncStrong算是智能指针中比较长的一个函数,我们分段来看
bool RefBase::weakref_type::attemptIncStrong(const void* id){ incWeak(id); .......}
首先,不管三七二十一,先增加弱引用计数一次(增加强引用计数的时候弱引用计数也需要+1,上面incStrong也有这个操作。)
bool RefBase::weakref_type::attemptIncStrong(const void* id){ ........ weakref_impl* const impl = static_cast(this); int32_t curCount = impl->mStrong.load(std::memory_order_relaxed); ALOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", this); while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { // we're in the easy/common case of promoting a weak-reference // from an existing strong reference. if (impl->mStrong.compare_exchange_weak(curCount, curCount+1, std::memory_order_relaxed)) { break; } // the strong count has changed on us, we need to re-assert our // situation. curCount was updated by compare_exchange_weak. } .........}
然后获取当前强引用计数器的值,如果当前已经有其他强引用,那么表示对象一定存在,所以这里就试着将强引用引用计数+1。
有两个疑问点,while循环和 compare_exchange_weak方法,其实都是考虑到多线程的时候同时操作出现的问题。具体不分析了,需要研究atomic的方法。
bool RefBase::weakref_type::attemptIncStrong(const void* id){ .......... if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { // we're now in the harder case of either: // - there never was a strong reference on us // - or, all strong references have been released int32_t flags = impl->mFlags.load(std::memory_order_relaxed); if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { ........ } else { // this object has an "extended" life-time, i.e.: it can be // revived from a weak-reference only. // Ask the object's implementation if it agrees to be revived if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) { // it didn't so give-up. decWeak(id); return false; } // grab a strong-reference, which is always safe due to the // extended life-time. curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed); // If the strong reference count has already been incremented by // someone else, the implementor of onIncStrongAttempted() is holding // an unneeded reference. So call onLastStrongRef() here to remove it. // (No, this is not pretty.) Note that we MUST NOT do this if we // are in fact acquiring the first reference. if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) { impl->mBase->onLastStrongRef(id); } } } .......}
如果当前没有强引用的,那么就会分成两种情况,就是对象生命周期受谁的控制。
我们先看对象生命周期受弱引用控制,而现在我们正通过弱引用尝试升级到强引用,这就说明弱引用肯定不为0,那么对象一定是存在的。不过我们在RefBase中还是添加了一个方法,onIncStrongAttempted用来询问是否允许升级到强指针,默认返回true表示允许,但是部分对象可能不允许做弱引用升级(重写该方法,返回false即可),如果不允许,那么久弱引用-1(回退第一步操作),直接然后返回false。如果允许弱引用升级,那么就强引用+1。
bool RefBase::weakref_type::attemptIncStrong(const void* id){ ........... if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { // we're now in the harder case of either: // - there never was a strong reference on us // - or, all strong references have been released int32_t flags = impl->mFlags.load(std::memory_order_relaxed); if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { // this object has a "normal" life-time, i.e.: it gets destroyed // when the last strong reference goes away if (curCount <= 0) { // the last strong-reference got released, the object cannot // be revived. decWeak(id); return false; } // here, curCount == INITIAL_STRONG_VALUE, which means // there never was a strong-reference, so we can try to // promote this object; we need to do that atomically. while (curCount > 0) { if (impl->mStrong.compare_exchange_weak(curCount, curCount+1, std::memory_order_relaxed)) { break; } // the strong count has changed on us, we need to re-assert our // situation (e.g.: another thread has inc/decStrong'ed us) // curCount has been updated. } if (curCount <= 0) { // promote() failed, some other thread destroyed us in the // meantime (i.e.: strong count reached zero). decWeak(id); return false; } } else { .......... } } ......}
如果对象的生命周期受强引用控制,那么又会分成两个情况,当前的强引用计数是否为初始值,如果不是初始值,那么表示曾经有过强引用,并且现在没有了,这种情况下,对象已经被delete掉了,所以需要弱引用-1作回退,然后返回false。如果强引用计数等于初始值,表示对象还没有强引用,自然也没有被销毁,可以转换,所以强引用+1。这里有个问题,+1之后的值并不是 1,而是 1<<28,所以才有下面最后这段代码
bool RefBase::weakref_type::attemptIncStrong(const void* id){ ..... impl->addStrongRef(id);#if PRINT_REFS ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);#endif // curCount is the value of mStrong before we incremented it. // Now we need to fix-up the count if it was INITIAL_STRONG_VALUE. // This must be done safely, i.e.: handle the case where several threads // were here in attemptIncStrong(). // curCount > INITIAL_STRONG_VALUE is OK, and can happen if we're doing // this in the middle of another incStrong. The subtraction is handled // by the thread that started with INITIAL_STRONG_VALUE. if (curCount == INITIAL_STRONG_VALUE) { impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed); } return true;}
addStringRef是空实现,不管,关键是下面,如果之前没有过强引用,这里需要在强引用计数上减去1<<28。
至此,弱指针的分析也结束了。
强弱指针应用
说了那么多,如果不需要了解实现逻辑,用例子来说明是最直接的,下面还是直接引用了罗老师的例子(有所调整,去掉了ForeverClass,因为在android8.0中已经没有Forever的标志了)
#include#include #define INITIAL_STRONG_VALUE (1<<28)using namespace android;class WeightClass : public RefBase{public: void printRefCount() { int32_t strong = getStrongCount(); weakref_type* ref = getWeakRefs(); printf("-----------------------\n"); printf("Strong Ref Count: %d.\n", (strong == INITIAL_STRONG_VALUE ? 0 : strong)); printf("Weak Ref Count: %d.\n", ref->getWeakCount()); printf("-----------------------\n"); }};class StrongClass : public WeightClass{public: StrongClass() { printf("Construct StrongClass Object.\n"); } virtual ~StrongClass() { printf("Destory StrongClass Object.\n"); }};class WeakClass : public WeightClass{public: WeakClass() { extendObjectLifetime(OBJECT_LIFETIME_WEAK); printf("Construct WeakClass Object.\n"); } virtual ~WeakClass() { printf("Destory WeakClass Object.\n"); }};void TestStrongClass(StrongClass* pStrongClass){ wp wpOut = pStrongClass; pStrongClass->printRefCount(); { sp spInner = pStrongClass; pStrongClass->printRefCount(); } sp spOut = wpOut.promote(); printf("spOut: %p.\n", spOut.get());}void TestWeakClass(WeakClass* pWeakClass){ wp wpOut = pWeakClass; pWeakClass->printRefCount(); { sp spInner = pWeakClass; pWeakClass->printRefCount(); } pWeakClass->printRefCount(); sp spOut = wpOut.promote(); printf("spOut: %p.\n", spOut.get());}int main(int argc, char** argv){ printf("Test Strong Class: \n"); StrongClass* pStrongClass = new StrongClass(); TestStrongClass(pStrongClass);//Test Strong Class: //Construct StrongClass Object. //----------------------- //Strong Ref Count: 0. //Weak Ref Count: 1. //----------------------- //----------------------- //Strong Ref Count: 1. //Weak Ref Count: 2. //----------------------- //Destory StrongClass Object. //spOut: 0x0. printf("\nTest Weak Class: \n"); WeakClass* pWeakClass = new WeakClass(); TestWeakClass(pWeakClass);//Test Weak Class: //Construct WeakClass Object. //----------------------- //Strong Ref Count: 0. //Weak Ref Count: 1. //----------------------- //----------------------- //Strong Ref Count: 1. //Weak Ref Count: 2. //----------------------- //----------------------- //Strong Ref Count: 0. //Weak Ref Count: 1. //----------------------- //spOut: 0xa528. //Destory WeakClass Object. return 0;}
WeightClass是基类,主要用于输出当前引用计数的数量。定义了WeakClass 和StringClass 让他们的生命周期分别受控于弱引用计数和强引用计数。
TestStringClass中,首先创建了一个弱指针,所以输出弱引用计数+1,强引用计数不变。然后在局部作用域中创建了一个强指针,很明显强引用和弱引用都会+1,当退出局部作用域,强指针自动释放,所以强弱指针都会-1,这个时候强指针计数变成了0,那么对象就会被自动delete,会输出析构函数中的打赢,所以当我们尝试将弱指针提升为强指针,应该会返回false,升级失败,所以最后输出一个空地址。最后退出,最后一个弱引用自动销毁,所以弱引用计数变为0,引用计数器类(weakref_impl)会被释放,不会出现内存泄漏。
TestWeakClass中,操作几乎和TestStringClass相同,不同点发生在退出局部作用域,这个时候虽然强引用变为0,但是弱引用并不是0,由于对象生命周期受弱引用计数控制,所以对象不会被销毁,当我们尝试升级弱引用的时候自然能够成功。当退出函数,弱引用和强引用都自动被释放,这个时候对象才会被销毁。