热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android智能指针spwp详解

nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd

研究Android的时候,经常会遇到sp、wp的东西,网上一搜,原来是android封装了c++中对象回收机制。
说明:
1. 如果一个类想使用智能指针,那么必须满足下面两个条件:
    a. 该类是虚基类RefBase的子类或间接子类
    b. 该类必须定义虚构造函数。如virtual ~MyClass();
2. 本文以类BBinder来进行说明,其余类使用sp或wp的情况类似
3. 代码路径:frameworks/base/libs/utils/RefBase.cpp
       frameworks/base/include/utils/RefBase.h

一、calss BBinder类说明
      class RefBase
      class IBinder
 class BpBinder   class BBinder
 class BBinder : public IBinder
 {
 ...
 protected:
     virtual             ~BBinder();
 ...
 }
 class IBinder : public virtual RefBase
 {
 ...
 protected:
     inline virtual      ~IBinder() { }
 ...
 }
 由上,可以看出BBinder和IBinder都是以public的方式继承于虚基类RefBase的。

二、sp wp对象的建立过程
 解析:sp  BB_ptr(new BBinder);
 这是一条定义sp指针BB_ptr的语句,他只想的对象是一个BBinder对象。
 如图所示。
 


 1》首先看一下new BBinder时都做了什么,特别是和该机制相关的初始化。
   c++中创建一个对象时,需要调用去构造函数,对于继承类,则是先调用其父类的构造函数,然后才会调用本身的
   构造函数。这里new一个BBinder对象时,顺序调用了:
    RefBase::RefBase() : mRefs(new weakref_impl(this)) {}
    inline   IBinder() {}
    BBinder::BBinder() : mExtras(NULL){}
   主要关注的是RefBase的构造函数,
   可以看出他是通过new weakref_impl(this)的结果来初始化私有成员mRefs
   这里的this指向BBinder对象自身,class weakref_impl继承于类RefBase的内嵌类weakref_type,然后该类
   weakref_impl又被类RefBase引用。类weakref_impl的构造函数如下:
   weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)    // 1 <<28
        , mWeak(0)
        , mBase(base)             // new BBinder指针
        , mFlags(0)
        , mStrongRefs(NULL)          // sp引用链表指针
        , mWeakRefs(NULL)           // wp引用链表指针
        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) // 1
        , mRetain(false) {}
  
 2》new BBinder返回的是BBinder对象的指针,如:sp  BB_ptr(0x????????);
   sp实际上是一个类模板,这条语句最终是要建立一个sp的实例化对象,叫模板类BB_ptr
   这里生成BB_ptr对象所调用的构造函数是:
   template
   sp::sp(T* other)
       : m_ptr(other)
   {
       if (other) other->incStrong(this);
   }
   BB_ptr对象的私有指针指向刚刚前面生成的BBinder对象。
   接着调用函数incStrong(),该函数是RefBase类的成员函数,在子类中没有被重载,所以这里
   other->incStrong(this)的调用实际上是调用基类成员函数incStrong(this),这个this值是指向sp对象
   BB_ptr的指针。现在转去查看该成员函数的实现。
   
   void RefBase::incStrong(const void* id) const
   {
       weakref_impl* const refs = mRefs;
       /* 取得BBinder对象基类中的私有只读指针mRefs */
       refs->addWeakRef(id);
       /* 调用weakref_impl类定义时实现的成员函数addWeakRef, 见下注释1*/
       refs->incWeak(id);
       /* 调用weakref_impl类的基类weakref_type成员函数incWeak, 见下注释2*/
      
       refs->addStrongRef(id);
       // 调用weakref_impl类定义时实现的成员函数addStrongRef, 见下注释1
       const int32_t c = android_atomic_inc(&refs->mStrong);
     /* 该函数实际将refs->mStrong值加1,也就是增加强引用计数值。但是返回值为refs->mStrong-1 */
       LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
   #if PRINT_REFS
       LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
   #endif
       if (c != INITIAL_STRONG_VALUE)  {
           return;
       }
     /* c = INITIAL_STRONG_VALUE, 第一个强引用产生的时候才会出现这个情况 */
       android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
     /* 返回值为INITIAL_STRONG_VALUE,refs->mStrong值变成1 */
       const_cast(this)->onFirstRef();
   }
   
/************************注释1********************************/
void addWeakRef(const void* id)
{
    addRef(&mWeakRefs, id, mWeak);
}
void addStrongRef(const void* id)
{
    addRef(&mStrongRefs, id, mStrong);
}
addRef()是类weakref_impl的私有成员函数,addWeakRef()函数引用的是public成员变量,而addRef()函数可以操作私有数据。

    struct ref_entry
    {
        ref_entry* next;
        const void* id;
        int32_t ref;
    };
   
void addRef(ref_entry** refs, const void* id, int32_t mRef)
    {
        if (mTrackEnabled) {
            AutoMutex _l(mMutex);
            ref_entry* ref = new ref_entry;
            ref->ref = mRef;
            ref->id = id;
           
            ref->next = *refs;
            *refs = ref;
   /*
   新出现的ref_entry结构体加入到链表头上,如果有n个sp指针指向同一个目标对象
   那么这里就有n个ref_entry结构体加入到这个单链表中,该结构体记录着如下数据
   1. id域记录着对应的sp强指针类对象的this值
   2. ref域记录的是当前sp强指针类对象是第几个引用目标对象的指针
   3. next域指向下一个指向目标对象的sp强指针对应的ref_entry结构体
   
   类RefBase的嵌套类weakref_type的子类的私有数据mRefs的私有二级指针成员mWeakRefs指向的是
   最后一个sp强指针对应的ref_entry结构体指针。

   总结一下:
   一个目标对象,可能被n个sp强指针指向,那么就存在n个class sp对象,同时每一个sp
   对象在目标对象的虚基类对象的成员类mRefs的私有二级指针成员mWeakRefs登记了一个
   ref_entry结构体,这些ref_entry结构体的地址都是由该链表管理,每一个
   ref_entry结构体和哪一个sp对象对应,也由该链表管理。同时链接数就是该链表节点的
   个数
   */
        }
    }
/************************注释1********************************/

/************************注释2********************************/
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast(this);
    // 强制类型转换,将基类指针转换成子类指针
    impl->addWeakRef(id); 
    // 调用类weakref_impl成员函数addWeakRef(),产生一个ref_entry结构体挂载mWeakRefs链表上
    const int32_t c = android_atomic_inc(&impl->mWeak);
  /* impl->mWeak加1,表示已存在一个weak引用。但返回值c为操作前的结果 */
    LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
/************************注释2********************************/

 3》上面是定义一个sp指针,下面看看定义一个wp指针式如何实现的。
     wp  BB_wp_ptr(BB_ptr);
    下面是wp类对应上面定义类型的构造函数
   template
   wp::wp(const sp& other)
       : m_ptr(other.m_ptr)
   {
       if (m_ptr) {
           m_refs = m_ptr->createWeak(this);
       }
   }
   this指针是指向wp对象的。createWeak()函数是RefBase类的成员函数。
   RefBase::weakref_type* RefBase::createWeak(const void* id) const
   {
       mRefs->incWeak(id);
       return mRefs;
   }
   mRefs指向的是第二步骤中产生的weakref_impl对象,调用基类weakref_type的成员函数incWeak()
   void RefBase::weakref_type::incWeak(const void* id)
   {
       weakref_impl* const impl = static_cast(this);
       impl->addWeakRef(id);
       const int32_t c = android_atomic_inc(&impl->mWeak);
     /* impl->mWeak有加1,但返回值为操作前的结果 */
       LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
   }
   


三、sp、wp释放过程
  sp BB_SP_ptr(BB_ptr);
  实际上BB_SP_ptr和前面的BB_ptr一样,指向的是同一个BBinder对象。另外需要注意的时,调用sp构造函数:
  template
  sp::sp(const sp& other)
      : m_ptr(other.m_ptr)
  {
      if (m_ptr) m_ptr->incStrong(this);
  }
  同样是需要调用BBinder对象的incStrong()函数,使用weakref_impl对象来管理新添加进来的强引用,同时增加一个
  ref_entry结构体到weakref_impl对象的mStrongRefs,增加2个ref_entry结构体到weakref_impl对象的mWeakRefs。
  如上图所示。
  
  现在来看看释放sp、wp指针的情况。
  delete BB_SP_ptr;
  将会调用如下形式的sp析构函数:
  template
  sp::~sp()
  {
      if (m_ptr) m_ptr->decStrong(this);
  }
  m_ptr指向的是前面生成的BBinder对象,调用其基类函数decStrong(this),this值是指向BB_SP_ptr对象。
  
  void RefBase::decStrong(const void* id) const
  {
      weakref_impl* const refs = mRefs;
      refs->removeStrongRef(id); // 注释3,移除mStrongRefs链表中和该sp对应的ref_entry结构体
      const int32_t c = android_atomic_dec(&refs->mStrong);
    /* 强引用计数减1, 但返回的是操作之前的引用计数值 */
      LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
      if (c == 1) {
       /*  c == 1说明刚刚removeStrongRef之前,整个系统中只存在一个sp对象引用目标对象,现在的情况就是
          系统中没有任何强指针对象来引用目标对象了,此时目标对象就会被删除释放
        */
          const_cast(this)->onLastStrongRef(id);
          if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
              delete this; // mFlags =0 ,条件成立,删除目标对象,这里就会删除前面new出来的BBinder对象
          }
      }// 如果此时还有其他指向该目标对象的sp指针存在的话,就不会删除目标对象
     
      refs->removeWeakRef(id);
      refs->decWeak(id);
      /* 删除新建目标对象sp指针时在mWeakRefs链表上增加的两个ref_entry结构体 */
  }
  /*********************************注释3*********************************/
  void removeStrongRef(const void* id)
    {
        if (!mRetain) // mRetain 初始化成 flase
            removeRef(&mStrongRefs, id); 
            /* 删除mStrongRefs链表中对应id的ref_entry一项 */
      /* 也就是取消了该sp对象和目标对象的联系 */
        else
            addRef(&mStrongRefs, id, -mStrong);
    }
  
  void removeRef(ref_entry** refs, const void* id)
    {
        if (mTrackEnabled) {
            AutoMutex _l(mMutex);
           
            ref_entry* ref = *refs;
            while (ref != NULL) {
                if (ref->id == id) {
                    *refs = ref->next;
                    delete ref;
                    return;
                }
                refs = &ref->next;
                ref = *refs;
            }
        }
    }
  /*********************************注释3*********************************/
  
  delete BB_wp_ptr;
  这是删除目标对象的一个wp指针,会调用wp的析构函数:
  template
  wp::~wp()
  {
      if (m_ptr) m_refs->decWeak(this);
  }
  调用weakref_type类的decWeak()函数,如下:
  void RefBase::weakref_type::decWeak(const void* id)
  {
      weakref_impl* const impl = static_cast(this);
      impl->removeWeakRef(id);// 移除weakref_impl对象mWeakRefs链表中对应id的ref_entry结构体
      const int32_t c = android_atomic_dec(&impl->mWeak);// 引用计数减1
      LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
      if (c != 1) return; // c == 1, 说明这是系统中存在的指向目标对象的最后一个wp指针
     
      if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
          if (impl->mStrOng== INITIAL_STRONG_VALUE)
              delete impl->mBase;
              // delete impl; 是不是应该加上这么一句,防止用户新建了wp后,不用,马上又删除的情况呢?
        /* 当目标对象的最后一个wp被析构时,如果目标对象还没有建立任何一个sp,那么目标对象被删除 */
          else {
              delete impl;
        /* 当目标对象的最后一个wp被析构时,但此时和目标对象相关的sp全部被析构,那么impl->mStrOng= 0
            在最后一个sp被析构的时候,目标对象也被释放,所以此时只需要释放weakref_impl对象即可
        */
          }
      } else {
          impl->mBase->onLastWeakRef(id);
          if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
              delete impl->mBase;
          }
      }
  }

四、wp升级为sp的过程
  wp的定义包含了:sp promote() const;
  template
  sp wp::promote() const
  {
      return sp(m_ptr, m_refs);
  }
  wp,sp互为友元类,这里promote就是以友元身份调用了sp类的构造函数: sp(T* p, weakref_type* refs);
  template
  sp::sp(T* p, weakref_type* refs)
      : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
  {
  }
  这里如果升级成功,那么将会产生一个sp对象指向目标对象,原来的wp仍然存在。
  如果升级不成功,返回NULL
  看看关键函数refs->attemptIncStrong(this)

  bool RefBase::weakref_type::attemptIncStrong(const void* id)
  {
      incWeak(id);
     
      weakref_impl* const impl = static_cast(this);
     
      int32_t curCount = impl->mStrong;
      LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow",
                 this);
      while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
          if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
              break;
          }
          curCount = impl->mStrong;
      }// 系统中还有其他sp指向目标对象的情况
     
      if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
          bool allow;
          if (curCount == INITIAL_STRONG_VALUE) {
          // 发现该目标对象还没有一个sp对象与之相关联的话,那么将会新建一个对目标对象的强引用
              allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
                    || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
          } else {
          /*
       发现系统中原来指向目标对象的sp全部被释放,最后一次sp释放也将目标对象释放了
      */
              allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
                    && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
          }
          if (!allow) {
              decWeak(id); // 目标对象已经不存在了,释放前面incWeak(id)产生的ref_entry结构体
              return false; 
          }
          curCount = android_atomic_inc(&impl->mStrong);
  
          if (curCount > 0 && curCount               impl->mBase->onLastStrongRef(id);
          }
      }
      // 走完生成一个sp的必要过程,和前面介绍的是一样
      impl->addWeakRef(id);
      impl->addStrongRef(id);
  
      if (curCount == INITIAL_STRONG_VALUE) {
          android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
          impl->mBase->onFirstRef();
      }
     
      return true; // 返回true
  }
  
 

五、总结:
  1. weakref_impl对象会随着目标对象的生成而产生,但不一定会随着目标对象的释放而释放。例如:如果目标对象被
    1个sp引用,但是同时被2个wp引用,那么在sp被删除的时候,删除了目标对象,但没有删除weakref_impl对象,
    只有在最后一个wp释放时,weakref_impl对象会被释放。
  2. 一个目标对象被多个sp指针引用,没有wp引用的情况下。释放这些sp的时候,delete会调用sp析构函数,
    然后调用RefBase类的成员函数decStrong(), 最后一个sp被释放时,weakref_impl对象数据成员mStrong会
    从1减到0(注意mStrong的初始化值为1<<28, 从这个值可以判断出该目标对象有没有被sp指针引用过),
    同时释放目标对象。
  3. 一个目标对象被多个wp指针引用,没有sp引用的情况下。delete这些wp的时候,会调用wp的析构函数,该函数会
    调用函数decWeak()。当删除最后一个wp的时候,代码中只是删除了目标对象,而没有释放weakref_impl对象,
    暂时没发现在哪里释放了它。
  4. 一个目标对象既有sp,又有wp来引用。如果sp先被删除光,那么最后一个sp删除的时候会释放掉目标对象,那么此时
    mStrOng= 0。在后续最后一个wp的释放过程中,在decWeak()函数中就会判断出impl->mStrong !=
    INITIAL_STRONG_VALUE,而释放掉剩下的weakref_impl对象了。如果先所以的wp删除光,此时mWeak还等于剩余的sp
    的个数,所以此时的释放情况,同第2小点的说明。
  5. 从wp定义来看,wp是不能直接操作对象的,必须先升级为sp才行。这个升级的过程是依靠函数promote()来完成的。
    升级成功,返回新生成的sp对象指针,升级失败,返回NULL。需要注意的是,如果目标对象之前有过sp指向,但后来
    将所有的sp释放完之后,此时目标对象是不存在的,那么此时用户还想将指向该目标对象的wp升级为sp的话,
    此时就返回NULL。那么这个时候我们应该delete这些剩下的wp。


推荐阅读
  • Python脚本编写创建输出数据库并添加模型和场数据的方法
    本文介绍了使用Python脚本编写创建输出数据库并添加模型数据和场数据的方法。首先导入相应模块,然后创建输出数据库并添加材料属性、截面、部件实例、分析步和帧、节点和单元等对象。接着向输出数据库中添加场数据和历程数据,本例中只添加了节点位移。最后保存数据库文件并关闭文件。文章还提供了部分代码和Abaqus操作步骤。另外,作者还建立了关于Abaqus的学习交流群,欢迎加入并提问。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Python语法上的区别及注意事项
    本文介绍了Python2x和Python3x在语法上的区别,包括print语句的变化、除法运算结果的不同、raw_input函数的替代、class写法的变化等。同时还介绍了Python脚本的解释程序的指定方法,以及在不同版本的Python中如何执行脚本。对于想要学习Python的人来说,本文提供了一些注意事项和技巧。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
  • 本文介绍了使用readlink命令获取文件的完整路径的简单方法,并提供了一个示例命令来打印文件的完整路径。共有28种解决方案可供选择。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
author-avatar
手机用户2502887971_699
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有