Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析(3)...

 提供引用计数器的类RefBase我们就暂时介绍到这里,后面我们再结合智能指针类一起分析,现在先来看看强指针类和弱指针类的定义。强指针类的定义我们在前面介绍轻量级指针的时候已经见到了,就是sp类了,这里就不再把它的代码列出来了。我们来看看它的构造函数的实现:

 
  1. template<typename T>   
  2. sp<T>::sp(T* other)   
  3.     : m_ptr(other)   
  4. {   
  5.     if (other) other->incStrong(this);   
  6. }   

  这里传进来的参数other一定是继承于RefBase类的,因此,在函数的内部,它调用的是RefBase类的incStrong函数,它定义在frameworks/base/libs/utils/RefBase.cpp文件中: 

 
  1. void RefBase::incStrong(const void* id) const   
  2. {   
  3.     weakref_impl* const refs = mRefs;   
  4.     refs->addWeakRef(id);   
  5.     refs->incWeak(id);   
  6.     refs->addStrongRef(id);    
  7.    
  8.     const int32_t c = android_atomic_inc(&refs->mStrong);    
  9.     LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);   
  10.    
  11.     #if PRINT_REFS    
  12.     LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);   
  13.     #endif    
  14.    
  15.     if (c != INITIAL_STRONG_VALUE) {    
  16.         return;    
  17.     }    
  18.    
  19.     android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);    
  20.     const_cast<RefBase*>(this)->onFirstRef();   
  21. }   

  成员变量mRefs是在RefBase类的构造函数中创建的:

 
  1. RefBase::RefBase()   
  2.     : mRefs(new weakref_impl(this))   
  3. {   
  4. //    LOGV("Creating refs %p with RefBase %p\n", mRefs, this);   
  5. }   

 在这个incStrong函数中,主要做了三件事情:

 一是增加弱引用计数:

 
  1. refs->addWeakRef(id);   
  2. refs->incWeak(id);   

 二是增加强引用计数:

 
  1. refs->addStrongRef(id);   
  2. const int32_t c = android_atomic_inc(&refs->mStrong);   

   三是如果发现是首次调用这个对象的incStrong函数,就会调用一个这个对象的onFirstRef函数,让对象有机会在对象被首次引用时做一些处理逻辑:

 
  1. if (c != INITIAL_STRONG_VALUE)  {   
  2.     return;   
  3. }   
  4.    
  5. android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);   
  6. const_cast<RefBase*>(this)->onFirstRef();   

 这里的c返回的是refs->mStrong加1前的值,如果发现等于INITIAL_STRONG_VALUE,就说明这个对象的强引用计数是第一次被增加,因此,refs->mStrong就是初始化为INITIAL_STRONG_VALUE的,它的值为:#define INITIAL_STRONG_VALUE (1<<28)  
   这个值加1后等于1<<28 + 1,不等于1,因此,后面要再减去-INITIAL_STRONG_VALUE,于是,refs->mStrong就等于1了,就表示当前对象的强引用计数值为1了,这与这个对象是第一次被增加强引用计数值的逻辑是一致的。
        回过头来看弱引用计数是如何增加的,首先是调用weakref_impl类的addWeakRef函数,我们知道,在Release版本中,这个函数也不做,而在Debug版本中,这个函数增加了一个ref_entry对象到了weakref_impl对象的mWeakRefs列表中,表示此weakref_impl对象的弱引用计数被增加了一次。接着又调用了weakref_impl类的incWeak函数,真正增加弱引用计数值就是在这个函数实现的了,weakref_impl类的incWeak函数继承于其父类weakref_type的incWeak函数:

 
  1. void RefBase::weakref_type::incWeak(const void* id)   
  2. {   
  3.     weakref_impl* const impl = static_cast<weakref_impl*>(this);   
  4.     impl->addWeakRef(id);   
  5.     const int32_t c = android_atomic_inc(&impl->mWeak);   
  6.     LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);   
  7. }   

  增加弱引用计数是下面语句执行的:const int32_t c = android_atomic_inc(&impl->mWeak);     但是前面为什么又调用了一次addWeakRef函数呢?前面不是已经调用过了吗?在Release版本中,因为weakref_impl类的addWeakRef函数是空实现,这里再调用一次没有什么害处,但是如果在Debug版本,岂不是冗余了吗?搞不清,有人问过负责开发Android系统Binder通信机制模块的作者Dianne Hackborn这个问题,他是这样回答的: 

 http://groups.google.com/group/android-platform/browse_thread/thread/cc641db8487dd83

        Ah I see.  Well the debug code may be broken, though I wouldn't leap to that 
        conclusion without actually testing it; I know it has been used in the 
        past.  Anyway, these things get compiled out in non-debug builds, so there 
        is no reason to change them unless you are actually trying to use this debug 
        code and it isn't working and need to do this to fix it. 

        既然他也不知道怎么回事,我们也不必深究了,知道有这么回事就行。

        这里总结一下强指针类sp在其构造函数里面所做的事情就是分别为目标对象的强引用计数和弱引和计数增加了1。

        再来看看强指针类的析构函数的实现:

 
  1. template<typename T>   
  2. sp<T>::~sp()   
  3. {   
  4.     if (m_ptr) m_ptr->decStrong(this);   
  5. }   

     同样,这里的m_ptr指向的目标对象一定是继承了RefBase类的,因此,这里调用的是RefBase类的decStrong函数,这也是定义在frameworks/base/libs/utils/RefBase.cpp文件中:

 
  1. void RefBase::decStrong(const void* id) const   
  2. {   
  3.     weakref_impl* const refs = mRefs;   
  4.     refs->removeStrongRef(id);   
  5.     const int32_t c = android_atomic_dec(&refs->mStrong);   
  6. #if PRINT_REFS   
  7.     LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);   
  8. #endif   
  9.     LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);   
  10.     if (c == 1) {   
  11.         const_cast<RefBase*>(this)->onLastStrongRef(id);   
  12.         if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {   
  13.             delete this;   
  14.         }   
  15.     }   
  16.     refs->removeWeakRef(id);   
  17.     refs->decWeak(id);   
  18. }   

  这里的refs->removeStrongRef函数调用语句是对应前面在RefBase::incStrong函数里的refs->addStrongRef函数调用语句的,在Release版本中,这也是一个空实现函数,真正实现强引用计数减1的操作是下面语句:

 
  1. const int32_t c = android_atomic_dec(&refs->mStrong);   

   如果发现减1前,此对象的强引用计数为1,就说明从此以后,就再没有地方引用这个目标对象了,这时候,就要看看是否要delete这个目标对象了:

 
  1. if (c == 1) {   
  2.     const_cast<RefBase*>(this)->onLastStrongRef(id);   
  3.     if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {   
  4.         delete this;   
  5.     }   
  6. }   

  在强引用计数为0的情况下,如果对象的标志位OBJECT_LIFETIME_WEAK被设置了,就说明这个对象的生命周期是受弱引用计数所控制的,因此,这时候就不能delete对象,要等到弱引用计数也为0的情况下,才能delete这个对象。

 

        接下来的ref->removeWeakRef函数调用语句是对应前面在RefBase::incStrong函数里的refs->addWeakRef函数调用语句的,在Release版本中,这也是一个空实现函数,真正实现强引用计数减1的操作下面的refs->decWeak函数,weakref_impl类没有实现自己的decWeak函数,它继承了weakref_type类的decWeak函数:

 
  1. void RefBase::weakref_type::decWeak(const void* id)   
  2. {   
  3.     weakref_impl* const impl = static_cast<weakref_impl*>(this);   
  4.     impl->removeWeakRef(id);   
  5.     const int32_t c = android_atomic_dec(&impl->mWeak);   
  6.     LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);   
  7.     if (c != 1) return;   
  8.    
  9.     if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {   
  10.         if (impl->mStrong == INITIAL_STRONG_VALUE)   
  11.             delete impl->mBase;   
  12.         else {   
  13. //            LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);   
  14.             delete impl;   
  15.         }   
  16.     } else {   
  17.         impl->mBase->onLastWeakRef(id);   
  18.         if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {   
  19.             delete impl->mBase;   
  20.         }   
  21.     }   
  22. }   

    这里又一次调用了weakref_impl对象的removeWeakRef函数,这也是和RefBase::weakref_type::incWeak函数里面的impl->addWeakRef语句所对应的,实现弱引用计数减1的操作是下面语句:const int32_t c = android_atomic_dec(&impl->mWeak);  

 减1前如果发现不等于1,那么就什么也不用做就返回了,如果发现等于1,就说明当前对象的弱引用计数值为0了,这时候,就要看看是否要delete这个对象了:

 
  1. if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {   
  2.     if (impl->mStrong == INITIAL_STRONG_VALUE)   
  3.         delete impl->mBase;   
  4.     else {   
  5. //      LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);   
  6.         delete impl;   
  7.     }   
  8. else {   
  9.     impl->mBase->onLastWeakRef(id);   
  10.     if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {   
  11.         delete impl->mBase;   
  12.     }   
  13. }   

  如果目标对象的生命周期是不受弱引用计数控制的,就执行下面语句:

 
  1. if (impl->mStrong == INITIAL_STRONG_VALUE)   
  2.     delete impl->mBase;   
  3. else {   
  4. //  LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);   
  5.     delete impl;   
  6. }   

     这个代码段是什么意思呢?这里是减少对象的弱引用计数的地方,如果调用到这里,那么就说明前面一定有增加过此对象的弱引用计数,而增加对象的弱引用计数有两种场景的,一种场景是增加对象的强引用计数的时候,会同时增加对象的弱引用计数,另一种场景是当我们使用一个弱指针来指向对象时,在弱指针对象的构造函数里面,也会增加对象的弱引用计数,不过这时候,就只是增加对象的弱引用计数了,并没有同时增加对象的强引用计数。因此,这里在减少对象的弱引用计数时,就要分两种情况来考虑。

 

        如果是前一种场景,这里的impl->mStrong就必然等于0,而不会等于INITIAL_STRONG_VALUE值,因此,这里就不需要delete目标对象了(impl->mBase),因为前面的RefBase::decStrong函数会负责delete这个对象。这里唯一需要做的就是把weakref_impl对象delete掉,但是,为什么要在这里delete这个weakref_impl对象呢?这里的weakref_impl对象是在RefBase的构造函数里面new出来的,理论上说应该在在RefBase的析构函数里delete掉这个weakref_impl对象的。在RefBase的析构函数里面,的确是会做这件事情:

 
  1. RefBase::~RefBase()   
  2. {   
  3. //    LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs);   
  4.     if (mRefs->mWeak == 0) {   
  5. //        LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this);   
  6.         delete mRefs;   
  7.     }   
  8. }   

   但是不要忘记,在这个场景下,目标对象是前面的RefBase::decStrong函数delete掉的,这时候目标对象就会被析构,但是它的弱引用计数值尚未执行减1操作,因此,这里的mRefs->mWeak == 0条件就不成立,于是就不会delete这个weakref_impl对象,因此,就延迟到执行这里decWeak函数时再执行。 

如果是后一种情景,这里的impl->mStrong值就等于INITIAL_STRONG_VALUE了,这时候由于没有地方会负责delete目标对象,因此,就需要把目标对象(imp->mBase)delete掉了,否则就会造成内存泄漏。在delete这个目标对象的时候,就会执行RefBase类的析构函数,这时候目标对象的弱引用计数等于0,于是,就会把weakref_impl对象也一起delete掉了。





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966562,如需转载请自行联系原作者

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279513.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ref:下一个项目为什么要用 SLF4J

ref:http://blog.mayongfa.cn/267.html 阿里巴巴 Java 开发手册 前几天阿里巴巴在云栖社区首次公开阿里官方Java代码规范标准&#xff0c;就是一个PDF手册&#xff0c;有命名规范&#xff0c;让你知道自己原来取的每一个类名、变量名都是烂名字&#xff0c;真替你家未来孩子担心…

洛谷P5055 【模板】可持久化文艺平衡树(FHQ Treap)

题面 传送门 题解 日常敲板子.jpg //minamoto #include<bits/stdc.h> #define R register #define inline __inline__ __attribute__((always_inline)) #define fp(i,a,b) for(R int i(a),I(b)1;i<I;i) #define fd(i,a,b) for(R int i(a),I(b)-1;i>I;--i) #define …

计算机突然蓝屏无法启动_为什么计算机无法立即启动?

计算机突然蓝屏无法启动With the newer, more powerful hardware and improved operating systems that we have available to use these days, why does it still take as long as it does to fully boot a computer up each time? 借助我们如今可以使用的更新&#xff0c;更…

CCNA课堂练习:OSPF的介绍及配置

CCNA浅谈OSPF的配置 今天我们来谈谈路由器OSPF的配置&#xff0c;那我先来介绍一下OSPF的特点&#xff1a;1、对网络发生的变化能够快速响应2、当网络发生变化的时候发送触发式更新•3、支持VLAN 4、管理方便ospf引用了区域的概念&#xff0c;区域分两种&#xff1a;1、骨干区域…

vcenter 6.7 (vcsa)部署指南

闲言少叙&#xff0c;直达心灵。 一、部署提要1.1 vCenter Server Appliance(VCSA )6.7下载地址https://pan.baidu.com/s/1WUShsC23E2qIIBg7MPR87w 6lzb 二、安装部署VCSA分为两个阶段安装&#xff0c;下面我们开始第一阶段2.1 打开之后&#xff0c;直接点击安装按钮2.2部署设备…

如何停止Internet Explorer 11的建议站点?

Internet Explorer automatically suggests addresses and search results based on the partial address you’re typing out. If this feature irritates you, read on as we learn how to turn it off. Internet Explorer会根据您键入的部分地址自动建议地址和搜索结果。 如…

什么是SG?+SG模板

先&#xff0c;定义一下 状态Position P 先手必败 N x先手必胜 操作方法&#xff1a; 反向转移 相同状态 不同位置 的一对 相当于无 对于ICG游戏&#xff0c;我们可以将游戏中每一个可能发生的局面表示为一个点。并且若存在局面i和局面j&#xff0c;且j是i的后继局面(即局面i可…

【桌面虚拟化】之三 Persistent vs NonP

作者&#xff1a;范军 &#xff08;Frank Fan&#xff09; 新浪微博&#xff1a;frankfan7 在【桌面虚拟化】之二类型及案例中我们探讨了桌面虚拟化的两种架构&#xff0c;HostedVirtual Desktop (VDI) 和 Published Desktop/App. 本文深入分析其中VDI的两种桌面类型&#xff0…

H5 video 开发问题及相关知识点

相关链接&#xff1a;H5 video 的使用H5 video 全屏播放♢ video点播与直播H5 video目前所有浏览器都支持的视频格式是MP4格式&#xff0c;所以mp4应当是点播web视频的首选格式。而在直播视频上&#xff0c;H5 video只在移动端原生支持HLS流的直播视频(Mac safari video标签也支…

Mybatis-Generator自动生成XML文件以及接口和实体类

整合了MySQL和Oracle配置文件生成方法 这个是整个文件夹的下载地址&#xff1a;http://www.codepeople.cn/download 主要给大家介绍一下generatorConfig.xml文件的配置&#xff0c;以及生成后的文件。 generatorConfig.xml <?xml version"1.0" encoding"UTF…

如何在Windows 10上设置默认Linux发行版

Windows 10 now allows you to install multiple Linux environments, starting with the Fall Creators Update. If you have multiple Linux environments, you can set your default and switch between them. Windows 10现在允许您从Fall Creators Update开始安装多个Linux…

mysql全备份+增量备份笔记总结

备份基础知识 冷备&#xff08;cold backup&#xff09;&#xff1a;需要关mysql服务&#xff0c;读写请求均不允许状态下进行&#xff1b; 温备&#xff08;warm backup&#xff09;&#xff1a; 服务在线&#xff0c;但仅支持读请求&#xff0c;不允许写请求&#xff1b; 热备…

pjax学习

PJAX 介绍 红薯 发布于 2012/04/11 22:06阅读 61K收藏 116评论 11jQuery.Pjax kissy开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f;->>> 介绍 pushState是一个可以操作history的api&#xff0c;该api的介绍和使用请见这里&#xff1a…

SQL Server 2000详细安装过程及配置

说明&#xff1a;这篇文章是几年前我发布在网易博客当中的原创文章&#xff0c;但由于网易博客现在要停止运营了&#xff0c;所以我就把这篇文章搬了过来&#xff0c;虽然现如今SQL Server 2000软件早已经过时了&#xff0c;但仍然有一部分人在使用它&#xff0c;尤其是某些高校…

移动应用ios和网页应用_如何在iOS上一次移动多个应用

移动应用ios和网页应用Apple doesn’t really believe in detailed instruction manuals, so some handy tricks slip through the cracks. One such trick we’ve recently discovered is that you can move multiple app icons at once on iOS. Here’s how. Apple并不真正相…

如何将内核静态库编译连接到驱动程序中去【转】

转自&#xff1a;http://blog.csdn.net/ganjianfeng2003/article/details/8089551 如何将内核静态库编译连接到驱动程序中去 2010-12-07 08:27 331人阅读 评论(1) 收藏 举报 http://blog.chinaunix.net/u2/61663/showart_2404744.html 刚上邮箱的时候发现一位网友向我询问这个问…

2018-2019 20165226 Exp9 Web安全基础

2018-2019 20165226 Exp9 Web安全基础 目录 一、实验内容说明及基础问题回答 二、实验过程 Webgoat准备XSS攻击 ① Phishing with XSS 跨站脚本钓鱼攻击② Stored XSS Attacks 存储型XSS攻击③ Reflected XSS Attacks 反射型XSS攻击 CSRF攻击 ① Cross Site Request Forgery(CS…

用 git 同步 Colab 与 Gitlab、Github 之间的文件

Colab 是谷歌提供的免费 Jupyter 服务&#xff0c;可使用 GPU。但由于每次的 VM &#xff08;虚拟机&#xff09;登出后所有文件都会连同&#xff36;&#xff2d;被毁掉。如何将一个项目里的程序或数据同步到 Colab则往往比较麻烦。尽管谷歌盘也可以挂到 Colab 里用&#xff0…

keep-alive使用_如何使用Google Keep进行无忧笔记

keep-alive使用There are a lot of note-taking apps out there. Google Keep may not be as powerful as services like Evernote, but its value is in its simplicity. Let’s talk about how to make the most of it. 那里有很多笔记应用程序。 Google Keep可能不如Evernot…

ZedGraph在项目中的应用

ZedGraph在项目中的应用将数据库数据提取出来&#xff0c;显示成曲线图&#xff08;饼状、柱状或立体图&#xff09;是项目中最常见的需求。 网上搜索到的解决方法&#xff0c;大多归为两类&#xff0c;一种是利用ActiveX组件&#xff0c;另一种是使用.net框架自带的画图的类。…