即如果目标对象的生命周期只受到强引用计数控制或者在目标对象的具体实现中总是允许这种情况发生。怎么理解呢?如果目标对象的生命周期只受强引用计数控制(它的标志位mFlags为0),而这时目标对象又还未被强指针引用过,它自然就不会被delete掉,因此,这时候可以判断出目标对象是存在的;如果目标对象的生命周期受弱引用计数控制(OBJECT_LIFETIME_WEAK),这时候由于目标对象正在被弱指针引用,因此,弱引用计数一定不为0,目标对象一定存在;如果目标对象的生命周期不受引用计数控制(BJECT_LIFETIME_FOREVER),这时候目标对象也是下在被弱指针引用,因此,目标对象的所有者必须保证这个目标对象还没有被delete掉,否则就会出问题了。在后面两种场景下,因为目标对象的生命周期都是不受强引用计数控制的,而现在又要把弱指针提升为强指针,就需要进一步调用目标对象的onIncStrongAttempted来看看是否允许这种情况发生,这又该怎么理解呢?可以这样理解,目标对象的设计者可能本身就不希望这个对象被强指针引用,只能通过弱指针来引用它,因此,这里它就可以重载其父类的onIncStrongAttempted函数,然后返回false,这样就可以阻止弱指针都被提升为强指针。在RefBase类中,其成员函数onIncStrongAttempted默认是返回true的:
- bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id)
- {
- return (flags&FIRST_INC_STRONG) ? true : false;
- }
如果此时目标对象的强引用计数值小于等于0,那就说明该对象之前一定被强指针引用过,这时候就必须保证目标对象是被弱引用计数控制的(BJECT_LIFETIME_WEAK),否则的话,目标对象就已经被delete了。同样,这里也要调用一下目标对象的onIncStrongAttempted成员函数,来询问一下目标对象在强引用计数值小于等于0的时候,是否允计将弱指针提升为强指针。下面这个代码段就是执行上面所说的逻辑:
- allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
- && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
继续往下看:
- if (!allow) {
- decWeak(id);
- return false;
- }
- curCount = android_atomic_inc(&impl->mStrong);
如果allow值为false,那么就说明不允计把这个弱指针提升为强指针,因此就返回false了,在返回之前,要先调用decWeak函数来减少目标对象的弱引用计数,因为函数的开头不管三七二十一,首先就调用了incWeak来增加目标对象的弱引用计数值。
函数attemptIncStrong的主体逻辑大概就是这样了,比较复杂,读者要细细体会一下。函数的最后,如果此弱指针是允计提升为强指针的,并且此目标对象是第一次被强指针引用,还需要调整一下目标对象的强引用计数值:
- if (curCount == INITIAL_STRONG_VALUE) {
- android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
- impl->mBase->onFirstRef();
- }
这个逻辑我们在前面分析强指针时已经分析过了,这里不再详述。
分析到这里,弱指针就介绍完了。强指针和弱指针的关系比较密切,同时它们也比较复杂,下面我们再举一个例子来说明强指针和弱指针的用法,同时也验证一下它们的实现原理。
5. 强指针和弱指针的用法
我们在external目录下建立一个C++工程目录weightpointer,它里面有两个文件,一个weightpointer.cpp文件,另外一个是Android.mk文件。
源文件weightpointer.cpp的内容如下:
- #include <stdio.h>
- #include <utils/RefBase.h>
- #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");
- }
- };
- class ForeverClass : public WeightClass
- {
- public:
- ForeverClass()
- {
- extendObjectLifetime(OBJECT_LIFETIME_FOREVER);
- printf("Construct ForeverClass Object.\n");
- }
- virtual ~ForeverClass()
- {
- printf("Destory ForeverClass Object.\n");
- }
- };
- void TestStrongClass(StrongClass* pStrongClass)
- {
- wp<StrongClass> wpOut = pStrongClass;
- pStrongClass->printRefCount();
- {
- sp<StrongClass> spInner = pStrongClass;
- pStrongClass->printRefCount();
- }
- sp<StrongClass> spOut = wpOut.promote();
- printf("spOut: %p.\n", spOut.get());
- }
- void TestWeakClass(WeakClass* pWeakClass)
- {
- wp<WeakClass> wpOut = pWeakClass;
- pWeakClass->printRefCount();
- {
- sp<WeakClass> spInner = pWeakClass;
- pWeakClass->printRefCount();
- }
- pWeakClass->printRefCount();
- sp<WeakClass> spOut = wpOut.promote();
- printf("spOut: %p.\n", spOut.get());
- }
- void TestForeverClass(ForeverClass* pForeverClass)
- {
- wp<ForeverClass> wpOut = pForeverClass;
- pForeverClass->printRefCount();
- {
- sp<ForeverClass> spInner = pForeverClass;
- pForeverClass->printRefCount();
- }
- }
- int main(int argc, char** argv)
- {
- printf("Test Strong Class: \n");
- StrongClass* pStrongClass = new StrongClass();
- TestStrongClass(pStrongClass);
- printf("\nTest Weak Class: \n");
- WeakClass* pWeakClass = new WeakClass();
- TestWeakClass(pWeakClass);
- printf("\nTest Froever Class: \n");
- ForeverClass* pForeverClass = new ForeverClass();
- TestForeverClass(pForeverClass);
- pForeverClass->printRefCount();
- delete pForeverClass;
- return 0;
- }
首先定义了一个基类WeightClass,继承于RefBase类,它只有一个成员函数printRefCount,作用是用来输出引用计数。接着分别定义了三个类StrongClass、WeakClass和ForeverClass,其中实例化StrongClass类的得到的对象的标志位为默认值0,实例化WeakClass类的得到的对象的标志位为OBJECT_LIFETIME_WEAK,实例化ForeverClass类的得到的对象的标志位为OBJECT_LIFETIME_FOREVER,后两者都是通过调用RefBase类的extendObjectLifetime成员函数来设置的。
在main函数里面,分别实例化了这三个类的对象出来,然后分别传给TestStrongClass函数、TestWeakClass函数和TestForeverClass函数来说明智能指针的用法,我们主要是通过考察它们的强引用计数和弱引用计数来验证智能指针的实现原理。
编译脚本文件Android.mk的内容如下:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := weightpointer
- LOCAL_SRC_FILES := weightpointer.cpp
- LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils
- include $(BUILD_EXECUTABLE)
最后,我们参照如何单独编译Android源代码中的模块一文,使用mmm命令对工程进行编译:
- USER-NAME@MACHINE-NAME:~/Android$ mmm ./external/weightpointer
编译之后,就可以打包了:
- USER-NAME@MACHINE-NAME:~/Android$ make snod
最后得到可执行程序weightpointer就位于设备上的/system/bin/目录下。启动模拟器,通过adb shell命令进入到模拟器终端,进入到/system/bin/目录,执行weightpointer可执行程序,验证程序是否按照我们设计的逻辑运行:
- USER-NAME@MACHINE-NAME:~/Android$ adb shell
- root@android:/ # cd system/bin/
- root@android:/system/bin # ./weightpointer
执行TestStrongClass函数的输出为:
- 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.
在TestStrongClass函数里面,首先定义一个弱批针wpOut指向从main函数传进来的StrongClass对象,这时候我们可以看到StrongClass对象的强引用计数和弱引用计数值分别为0和1;接着在一个大括号里面定义一个强指针spInner指向这个StrongClass对象,这时候我们可以看到StrongClass对象的强引用计数和弱引用计数值分别为1和2;当程序跳出了大括号之后,强指针spInner就被析构了,从上面的分析我们知道,强指针spInner析构时,会减少目标对象的强引用计数值,因为前面得到的强引用计数值为1,这里减1后,就变为0了,又由于这个StrongClass对象的生命周期只受强引用计数控制,因此,这个StrongClass对象就被delete了,这一点可以从后面的输出(“Destory StrongClass Object.”)以及试图把弱指针wpOut提升为强指针时得到的对象指针为0x0得到验证。
执行TestWeakClass函数的输出为:
- 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.
TestWeakClass函数和TestStrongClass函数的执行过程基本一样,所不同的是当程序跳出大括号之后,虽然这个WeakClass对象的强引用计数值已经为0,但是由于它的生命周期同时受强引用计数和弱引用计数控制,而这时它的弱引用计数值大于0,因此,这个WeakClass对象不会被delete掉,这一点可以从后面试图把弱批针wpOut提升为强指针时得到的对象指针不为0得到验证。
执行TestForeverClass函数的输出来:
- Test Froever Class:
- Construct ForeverClass Object.
- -----------------------
- Strong Ref Count: 0.
- Weak Ref Count: 1.
- -----------------------
- -----------------------
- Strong Ref Count: 1.
- Weak Ref Count: 2.
- -----------------------
当执行完TestForeverClass函数返回到main函数的输出来:
- Test Froever Class:
- Construct ForeverClass Object.
- -----------------------
- Strong Ref Count: 0.
- Weak Ref Count: 1.
- -----------------------
- -----------------------
- Strong Ref Count: 1.
- Weak Ref Count: 2.
- -----------------------
当执行完TestForeverClass函数返回到main函数的输出来:
- -----------------------
- Strong Ref Count: 0.
- Weak Ref Count: 0.
- -----------------------
- Destory ForeverClass Object.
这里我们可以看出,虽然这个ForeverClass对象的强引用计数和弱引用计数值均为0了,但是它不自动被delete掉,虽然由我们手动地delete这个对象,它才会被析构,这是因为这个ForeverClass对象的生命周期是既不受强引用计数值控制,也不会弱引用计数值控制。
这样,从TestStrongClass、TestWeakClass和TestForeverClass这三个函数的输出就可以验证了我们上面对Android系统的强指针和弱指针的实现原理的分析。
至此,Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理就分析完成了,它实现得很小巧但是很精致,希望读者可以通过实际操作细细体会一下。