Android 智能指针
1. 由来
为了解决C++指针使用过程中遇到的内存泄漏和无效引用问题,Android 系统引入了智能指针。
智能指针是一种能够自动维护对象引用计数的技术。智能指针是一个对象,而不是一个指针,但是它引用了一个实际使用的对象。正是因为它是一个对象,因此它能够自动地维护实际对象的引用计数。简单来说:
- 在智能指针构造时,就增加它所引用的对象的引用计数。
- 在智能指针析构时,就减少它所引用的对象的引用计数。
由于C++中智能指针的构造函数和析构函数都是自动执行的,因此,它就很自然的实现了自动的对象引用计数技术。
为了解决引用计数器的归属问题,计数器只能由实际使用的对象(被智能指针引用的目标对象)自身持有。
为了解决释放时相互引用的问题,需要明确引用关系的强和弱,即需要区分对象的强引用计数和弱引用计数,并且规定对象的生命周期应该受强引用控制,这样可以打破这种循环引用的带来的问题。
2. 类别和原理分析
智能指针分为三类:
-
轻量级指针(Light Pointer)
轻量级指针通过简单的引用计数技术来维护对象的生命周期。如果一个类的对象支持轻量级指针,那么它必须从LightRefBase类继承下来,因为LightRefBase类提供了一个简单的引用计数器。LightRefBase类只有一个成员变量mCount,用来描述一个对象的引用计数值。LightRefBase类同时提供了成员函数incStrong和decStrong来增加和减少它所引用的对象的引用计数。在成员函数decStrong中,如果对象的引用计数值在减少之后变成0,那么就表示需要释放这个对象所占用的内存了。
-
强指针(Strong Pointer)
强指针通过强引用计数来维护对象的生命周期。如果一个类的对象要支持强指针,那么它就必须从RefBase类继承下来,因为RefBase类提供了强引用计数器和弱引用计数器。
RefBase类与LightRefBase类不一样的是,它不是直接使用一个整数来维护对象的引用计数的,而是使用一个weakref_impl对象,及成员变量mRefs来描述对象的引用计数。 weakref_impl类同时为对象提供了强引用计数和弱引用计数,它的源码如下所示(/system/core/libutils/RefBase.cpp)
weakref_impl类继承了weakref_type类。weakref_type类定义在RefBase类的内部,它提供了成员函数incWeak、decWeak、attemptIncStrong和attemptIncWeak来维护对象的强引用计数和弱引用计数。weakref_type类只定义了引用计数维护接口,具体的实现是由weakref_impl类提供的。 weakref_impl类有两个成员变量mStrong和mWeak,分别用了描述对象的强引用计数和弱引用计数。同时,weakref_impl类的成员变量mBase指向了它所引用的对象的地址,而成员变量mFlags是一个标志值,用了描述对象的生命周期控制方式。 weakref_impl类的成员变量mFlags的取值范围为0和OBJECT_LIEFTIME_WEAK。其中:
0:表示对象的生命周期只受强引用计数的影响。
OBJECT_LIFETIME_WEAK:表示对象的生命周期同时受强引用计数和弱引用计数影响 。
所以可以得出结论:
(1)如果一个对象的生命周期控制标志值被设置为0,那么只要它的强引用计数值为0,系统就会自动释放这个对象。
(2)如果 一个对象的生命周期控制标志值被设置为OBJECT_LIFETIME_WEAK,那么只有当它的强引用计数值和弱引用计数值都为0时,系统才会释放掉这个对象。
-
弱指针(Weak Pointer)
如果一个类的对象支持使用弱指针,那么这个类就必须要从RefBase类继承下来,因为RefBase类提供了弱引用计数器。
与强指针sp类类似,弱指针类wp也有一个成员变量m_ptr,用来指向它所引用的对象,但是弱指针类wp还使用另外一个类型为weakref_type*的成员变量m_refs,用来维护对象的弱引用计数。
因为弱指针所引用的对象可能是不受弱引用计数控制的,即它所引用的对象可能是一个无效的对象,且弱指针没有重载->,* 等运算符,所以不能通过弱指针直接操作它所引用的对象。如果需要操作一个弱指针所引用的对象,那么就需要将这个弱指针升级为强指针,这是通过调用它的成员函数promote来实现的。如果升级成功,就说明该弱指针所引用的对象还没有被销毁,可以正常使用。
一个弱指针所引用的对象可能处于两种状态:
第一种:该对象同时也被其他强指针对象所引用,此时可以安全地将这个弱指针升级为强指针。
第二种:该对象没有被任何强指针引用。这里情况就比较复杂了。需要根据对象生命周期来判断
1. 如果对象生命周期只受强引用计数影响,那么就可以成功将该弱指针升级为强指针。因为它受强引用计数影响,而 此时该对象又没有被强指针引用过,那么它必然不会被释放。
2. 如果只受弱引用计数影响,首先我们可以确定对象现在一定是存在的,因为现在有一个弱指针引用它。但是,这种情况需要进一步调用对象的成员函数onIncStrongAttempted来确认对象是否允许强指针引用它。如果返回为true说明允许则成功将该弱指针升级为强指针。如果返回为false,则说明升级失败。
其他参考资料:
https://blog.csdn.net/wzy_1988/article/details/43736137
https://www.jianshu.com/p/f131b299df66