`
snake_hand
  • 浏览: 639740 次
社区版块
存档分类
最新评论

Android 智能指针原理

 
阅读更多

Android系统的运行时库层代码是用C++来编写的,用C++来写代码最容易出错的地方就是指针了,一旦使用不当,轻则造成内存泄漏,重则造成系统崩溃。不过系统为我们提供了智能指针,避免出现上述问题,本文将系统地分析Android系统智能指针(轻量级指针、强指针和弱指针)的实现原理。


一、内存泄露与智能指针

在使用C++来编写代码的过程中,指针使用不当造成内存泄漏一般就是因为new了一个对象并且使用完之后,忘记了delete这个对象,而造成系统崩溃。一般就是因为一个地方delete了这个对象之后,其它地方还在继续使原来指向这个对象的指针。

为了避免出现上述问题,一般的做法就是使用引用计数的方法,每当有一个指针指向了一个new出来的对象时,就对这个对象的引用计数增加1,每当有一个指针不再使用这个对象时,就对这个对象的引用计数减少1,每次减1之后,如果发现引用计数值为0时,那么,就要delete这个对象了,这样就避免了忘记delete对象或者这个对象被delete之后其它地方还在使用的问题了。

但是,如何实现这个对象的引用计数呢?肯定不是由开发人员来手动地维护了,要开发人员时刻记住什么时候该对这个对象的引用计数加1,什么时候该对这个对象的引用计数减1,一来是不方便开发,二来是不可靠,一不小心哪里多加了一个1或者多减了一个1,就会造成灾难性的后果。这时候,智能指针就粉墨登场了。

首先,智能指针是一个对象,不过这个对象代表的是另外一个真实使用的对象,当智能指针指向实际对象的时候,就是智能指针对象创建的时候,当智能指针不再指向实际对象的时候,就是智能指针对象销毁的时候,我们知道,在C++中,对象的创建和销毁时会分别自动地调用对象的构造函数和析构函数,这样,负责对真实对象的引用计数加1和减1的工作就落实到智能指针对象的构造函数和析构函数的身上了,这也是为什么称这个指针对象为智能指针的原因。

在计算机科学领域中,提供垃圾收集GC(Garbage Collection)功能的系统框架,即提供对象托管功能的系统框架,例如Java应用程序框架,也是采用上述的引用计数技术方案来实现的,然而,简单的引用计数技术不能处理系统中对象间循环引用的情况。考虑这样的一个场景,系统中有两个对象A和B,在对象A的内部引用了对象B,而在对象B的内部也引用了对象A。当两个对象A和B都不再使用时,垃圾收集系统会发现无法回收这两个对象的所占据的内存的,因为系统一次只能收集一个对象,而无论系统决定要收回对象A还是要收回对象B时,都会发现这个对象被其它的对象所引用,因而就都回收不了,这样就造成了内存泄漏。

这样,就要采取另外的一种引用计数技术了,即对象的引用计数同时存在强引用和弱引用两种计数,例如,Apple公司提出的Cocoa框架,当父对象要引用子对象时,就对子对象使用强引用计数技术,而当子对象要引用父对象时,就对父对象使用弱引用计数技术,而当垃圾收集系统执行对象回收工作时,只要发现对象的强引用计数为0,而不管它的弱引用计数是否为0,都可以回收这个对象,但是,如果我们只对一个对象持有弱引用计数,当我们要使用这个对象时,就不直接使用了,必须要把这个弱引用升级成为强引用时,才能使用这个对象,在转换的过程中,如果对象已经不存在,那么转换就失败了,这时候就说明这个对象已经被销毁了,不能再使用了。



二、Android 智能指针

了解了这些背景知识后,我们就可以进一步学习Android系统的智能指针的实现原理了。Android系统提供了强大的智能指针技术供我们使用,这些智能指针实现方案既包括简单的引用计数技术,也包括了复杂的引用计数技术,即对象既有强引用计数,也有弱引用计数,对应地,这三种智能指针分别就称为轻量级指针(Light Pointer)、强指针(Strong Pointer)、弱指针(Weak Pointer)。无论是轻量级指针,还是强指针和弱指针,它们的实现框架都是一致的,即由对象本身来提供引用计数器,但是它不会去维护这个引用计数器的值,而是由智能指针来维护,就好比是对象提供素材,但是具体怎么去使用这些素材,就交给智能指针来处理了。由于不管是什么类型的对象,它都需要提供引用计数器这个素材,在C++中,我们就可以把这个引用计数器素材定义为一个公共类,这个类只有一个成员变量,那就是引用计数成员变量,其它提供智能指针引用的对象,都必须从这个公共类继承下来,这样,这些不同的对象就天然地提供了引用计数器给智能指针使用了。总的来说就是我们在实现智能指会的过程中,第一是要定义一个负责提供引用计数器的公共类,第二是我们要实现相应的智能指针对象类,后面我们会看到这种方案是怎么样实现的。

接下来,我们就先介绍轻量级指针的实现原理,然后再接着介绍强指针和弱指针的实现原理。


1. 轻量级指针

先来看一下实现引用计数的类LightRefBase,它定义在frameworks/base/include/utils/RefBase.h文件中:

  1. template<classT>
  2. classLightRefBase
  3. {
  4. public:
  5. inlineLightRefBase():mCount(0){}
  6. inlinevoidincStrong(constvoid*id)const{
  7. android_atomic_inc(&mCount);
  8. }
  9. inlinevoiddecStrong(constvoid*id)const{
  10. if(android_atomic_dec(&mCount)==1){
  11. deletestatic_cast<constT*>(this);
  12. }
  13. }
  14. //!DEBUGGINGONLY:Getcurrentstrongrefcount.
  15. inlineint32_tgetStrongCount()const{
  16. returnmCount;
  17. }
  18. protected:
  19. inline~LightRefBase(){}
  20. private:
  21. mutablevolatileint32_tmCount;
  22. };
这个类很简单,它只一个成员变量mCount,这就是引用计数器了,它的初始化值为0,另外,这个类还提供两个成员函数incStrong和decStrong来维护引用计数器的值,这两个函数就是提供给智能指针来调用的了,这里要注意的是,在decStrong函数中,如果当前引用计数值为1,那么当减1后就会变成0,于是就会delete这个对象。

前面说过,要实现自动引用计数,除了要有提供引用计数器的基类外,还需要有智能指针类。在Android系统中,配合LightRefBase引用计数使用的智能指针类便是sp了,它也是定义在frameworks/base/include/utils/RefBase.h文件中:

  1. template<typenameT>
  2. classsp
  3. {
  4. public:
  5. typedeftypenameRefBase::weakref_typeweakref_type;
  6. inlinesp():m_ptr(0){}
  7. sp(T*other);
  8. sp(constsp<T>&other);
  9. template<typenameU>sp(U*other);
  10. template<typenameU>sp(constsp<U>&other);
  11. ~sp();
  12. //Assignment
  13. sp&operator=(T*other);
  14. sp&operator=(constsp<T>&other);
  15. template<typenameU>sp&operator=(constsp<U>&other);
  16. template<typenameU>sp&operator=(U*other);
  17. //!SpecialoptimizationforusebyProcessState(andnobodyelse).
  18. voidforce_set(T*other);
  19. //Reset
  20. voidclear();
  21. //Accessors
  22. inlineT&operator*()const{return*m_ptr;}
  23. inlineT*operator->()const{returnm_ptr;}
  24. inlineT*get()const{returnm_ptr;}
  25. //Operators
  26. COMPARE(==)
  27. COMPARE(!=)
  28. COMPARE(>)
  29. COMPARE(<)
  30. COMPARE(<=)
  31. COMPARE(>=)
  32. private:
  33. template<typenameY>friendclasssp;
  34. template<typenameY>friendclasswp;
  35. //Optimizationforwp::promote().
  36. sp(T*p,weakref_type*refs);
  37. T*m_ptr;
  38. };
这个类的内容比较多,但是这里我们只关注它的成员变量m_ptr、构造函数和析构函数。不难看出,成员变量m_ptr就是指向真正的对象了,它是在构造函数里面初始化的。接下来我们就再看一下它的两个构造函数,一个是普通构造函数,一个拷贝构造函数:

  1. template<typenameT>
  2. sp<T>::sp(T*other)
  3. :m_ptr(other)
  4. {
  5. if(other)other->incStrong(this);
  6. }
  7. template<typenameT>
  8. sp<T>::sp(constsp<T>&other)
  9. :m_ptr(other.m_ptr)
  10. {
  11. if(m_ptr)m_ptr->incStrong(this);
  12. }
这两个构造函数都会首先初始化成员变量m_ptr,然后再调用m_ptr的incStrong函数来增加对象的引用计数,在我们这个场景中,就是调用LightRefBase类的incStrong函数了。

最后,看一下析构函数:

  1. template<typenameT>
  2. sp<T>::~sp()
  3. {
  4. if(m_ptr)m_ptr->decStrong(this);
  5. }
析构函数也很简单,只是调用m_ptr的成员函数decStrong来减少对象的引用计数值,这里就是调用LightRefBase类的decStrong函数了,前面我们看到,当这个引用计数减1后变成0时,就会自动delete这个对象了。

轻量级智能指针的实现原理大概就是这样了,比较简单,下面我们再用一个例子来说明它的用法。


2. 轻量级指针的用法

参考在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序一文,我们在external目录下建立一个C++工程目录lightpointer,它里面有两个文件,一个lightpointer.cpp文件,另外一个是Android.mk文件。

源文件lightpointer.cpp的内容如下:

  1. #include<stdio.h>
  2. #include<utils/RefBase.h>
  3. usingnamespaceandroid;
  4. classLightClass:publicLightRefBase<LightClass>
  5. {
  6. public:
  7. LightClass()
  8. {
  9. printf("ConstructLightClassObject.");
  10. }
  11. virtual~LightClass()
  12. {
  13. printf("DestoryLightClassObject.");
  14. }
  15. };
  16. intmain(intargc,char**argv)
  17. {
  18. LightClass*pLightClass=newLightClass();
  19. sp<LightClass>lpOut=pLightClass;
  20. printf("LightRefCount:%d.\n",pLightClass->getStrongCount());
  21. {
  22. sp<LightClass>lpInner=lpOut;
  23. printf("LightRefCount:%d.\n",pLightClass->getStrongCount());
  24. }
  25. printf("LightRefCount:%d.\n",pLightClass->getStrongCount());
  26. return0;
  27. }
我们创建一个自己的类LightClass,继承了LightRefBase模板类,这样类LightClass就具有引用计数的功能了。在main函数里面,我们首先new一个LightClass对象,然后把这个对象赋值给智能指针lpOut,这时候通过一个printf语句来将当前对象的引用计数值打印出来,从前面的分析可以看出,如果一切正常的话,这里打印出来的引用计数值为1。接着,我们又在两个大括号里面定义了另外一个智能指针lpInner,它通过lpOut间接地指向了前面我们所创建的对象,这时候再次将当前对象的引用计数值打印出来,从前面 的分析也可以看出,如果一切正常的话,这里打印出来的引用计数值应该为2。程序继承往下执行,当出了大括号的范围的时候,智能指针对象lpInner就被析构了,从前面的分析可以知道,智能指针在析构的时候,会减少当前对象的引用计数值,因此,最后一个printf语句打印出来的引用计数器值应该为1。当main函数执行完毕后,智能指针lpOut也会被析构,被析构时,它会再次减少当前对象的引用计数,这时候,对象的引用计数值就为0了,于是,它就会被delete了。

编译脚本文件Android.mk的内容如下:

  1. LOCAL_PATH:=$(callmy-dir)
  2. include$(CLEAR_VARS)
  3. LOCAL_MODULE_TAGS:=optional
  4. LOCAL_MODULE:=lightpointer
  5. LOCAL_SRC_FILES:=lightpointer.cpp
  6. LOCAL_SHARED_LIBRARIES:=\
  7. libcutils\
  8. libutils
  9. include$(BUILD_EXECUTABLE)
最后,我们参照如何单独编译Android源代码中的模块一文,使用mmm命令对工程进行编译:
  1. USER-NAME@MACHINE-NAME:~/Android$mmm./external/lightpointer
编译之后,就可以打包了:

  1. USER-NAME@MACHINE-NAME:~/Android$makesnod
最后得到可执行程序lightpointer就位于设备上的/system/bin/目录下。启动模拟器,通过adb shell命令进入到模拟器终端,进入到/system/bin/目录,执行lightpointer可执行程序,验证程序是否按照我们设计的逻辑运行:
  1. USER-NAME@MACHINE-NAME:~/Android$adbshell
  2. root@android:/#cdsystem/bin/
  3. root@android:/system/bin#./lightpointer
  4. ConstructLightClassObject.
  5. LightRefCount:1.
  6. LightRefCount:2.
  7. LightRefCount:1.
  8. DestoryLightClassObject.

这里可以看出,程序一切都是按照我们的设计来运行,这也验证了我们上面分析的轻量级智能指针的实现原理。


3. 强指针

强指针所使用的引用计数类为RefBase,它LightRefBase类要复杂多了,所以才称后者为轻量级的引用计数基类吧。我们先来看看RefBase类的实现,它定义在frameworks/base/include/utils/RefBase.h文件中:

  1. classRefBase
  2. {
  3. public:
  4. voidincStrong(constvoid*id)const;
  5. voiddecStrong(constvoid*id)const;
  6. voidforceIncStrong(constvoid*id)const;
  7. //!DEBUGGINGONLY:Getcurrentstrongrefcount.
  8. int32_tgetStrongCount()const;
  9. classweakref_type
  10. {
  11. public:
  12. RefBase*refBase()const;
  13. voidincWeak(constvoid*id);
  14. voiddecWeak(constvoid*id);
  15. boolattemptIncStrong(constvoid*id);
  16. //!ThisisonlysafeifyouhavesetOBJECT_LIFETIME_FOREVER.
  17. boolattemptIncWeak(constvoid*id);
  18. //!DEBUGGINGONLY:Getcurrentweakrefcount.
  19. int32_tgetWeakCount()const;
  20. //!DEBUGGINGONLY:Printreferencesheldonobject.
  21. voidprintRefs()const;
  22. //!DEBUGGINGONLY:Enabletrackingforthisobject.
  23. //enable--enable/disabletracking
  24. //retain--whentrackingisenable,iftrue,thenwesaveastacktrace
  25. //foreachreferenceanddereference;whenretain==false,we
  26. //matchupreferencesanddereferencesandkeeponlythe
  27. //outstandingones.
  28. voidtrackMe(boolenable,boolretain);
  29. };
  30. weakref_type*createWeak(constvoid*id)const;
  31. weakref_type*getWeakRefs()const;
  32. //!DEBUGGINGONLY:Printreferencesheldonobject.
  33. inlinevoidprintRefs()const{getWeakRefs()->printRefs();}
  34. //!DEBUGGINGONLY:Enabletrackingofobject.
  35. inlinevoidtrackMe(boolenable,boolretain)
  36. {
  37. getWeakRefs()->trackMe(enable,retain);
  38. }
  39. protected:
  40. RefBase();
  41. virtual~RefBase();
  42. //!FlagsforextendObjectLifetime()
  43. enum{
  44. OBJECT_LIFETIME_WEAK=0x0001,
  45. OBJECT_LIFETIME_FOREVER=0x0003
  46. };
  47. voidextendObjectLifetime(int32_tmode);
  48. //!FlagsforonIncStrongAttempted()
  49. enum{
  50. FIRST_INC_STRONG=0x0001
  51. };
  52. virtualvoidonFirstRef();
  53. virtualvoidonLastStrongRef(constvoid*id);
  54. virtualboolonIncStrongAttempted(uint32_tflags,constvoid*id);
  55. virtualvoidonLastWeakRef(constvoid*id);
  56. private:
  57. friendclassweakref_type;
  58. classweakref_impl;
  59. RefBase(constRefBase&o);
  60. RefBase&operator=(constRefBase&o);
  61. weakref_impl*constmRefs;
  62. };
RefBase类和LightRefBase类一样,提供了incStrong和decStrong成员函数来操作它的引用计数器;而RefBase类与LightRefBase类最大的区别是,它不像LightRefBase类一样直接提供一个整型值(mutable volatile int32_t mCount)来维护对象的引用计数,前面我们说过,复杂的引用计数技术同时支持强引用计数和弱引用计数,在RefBase类中,这两种计数功能是通过其成员变量mRefs来提供的。

RefBase类的成员变量mRefs的类型为weakref_impl指针,它实现在frameworks/base/libs/utils/RefBase.cpp文件中:

  1. classRefBase::weakref_impl:publicRefBase::weakref_type
  2. {
  3. public:
  4. volatileint32_tmStrong;
  5. volatileint32_tmWeak;
  6. RefBase*constmBase;
  7. volatileint32_tmFlags;
  8. #if!DEBUG_REFS
  9. weakref_impl(RefBase*base)
  10. :mStrong(INITIAL_STRONG_VALUE)
  11. ,mWeak(0)
  12. ,mBase(base)
  13. ,mFlags(0)
  14. {
  15. }
  16. voidaddStrongRef(constvoid*/*id*/){}
  17. voidremoveStrongRef(constvoid*/*id*/){}
  18. voidaddWeakRef(constvoid*/*id*/){}
  19. voidremoveWeakRef(constvoid*/*id*/){}
  20. voidprintRefs()const{}
  21. voidtrackMe(bool,bool){}
  22. #else
  23. weakref_impl(RefBase*base)
  24. :mStrong(INITIAL_STRONG_VALUE)
  25. ,mWeak(0)
  26. ,mBase(base)
  27. ,mFlags(0)
  28. ,mStrongRefs(NULL)
  29. ,mWeakRefs(NULL)
  30. ,mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
  31. ,mRetain(false)
  32. {
  33. //LOGI("NEWweakref_impl%pforRefBase%p",this,base);
  34. }
  35. ~weakref_impl()
  36. {
  37. LOG_ALWAYS_FATAL_IF(!mRetain&&mStrongRefs!=NULL,"Strongreferencesremain!");
  38. LOG_ALWAYS_FATAL_IF(!mRetain&&mWeakRefs!=NULL,"Weakreferencesremain!");
  39. }
  40. voidaddStrongRef(constvoid*id)
  41. {
  42. addRef(&mStrongRefs,id,mStrong);
  43. }
  44. voidremoveStrongRef(constvoid*id)
  45. {
  46. if(!mRetain)
  47. removeRef(&mStrongRefs,id);
  48. else
  49. addRef(&mStrongRefs,id,-mStrong);
  50. }
  51. voidaddWeakRef(constvoid*id)
  52. {
  53. addRef(&mWeakRefs,id,mWeak);
  54. }
  55. voidremoveWeakRef(constvoid*id)
  56. {
  57. if(!mRetain)
  58. removeRef(&mWeakRefs,id);
  59. else
  60. addRef(&mWeakRefs,id,-mWeak);
  61. }
  62. voidtrackMe(booltrack,boolretain)
  63. {
  64. mTrackEnabled=track;
  65. mRetain=retain;
  66. }
  67. ......
  68. private:
  69. structref_entry
  70. {
  71. ref_entry*next;
  72. constvoid*id;
  73. #ifDEBUG_REFS_CALLSTACK_ENABLED
  74. CallStackstack;
  75. #endif
  76. int32_tref;
  77. };
  78. voidaddRef(ref_entry**refs,constvoid*id,int32_tmRef)
  79. {
  80. if(mTrackEnabled){
  81. AutoMutex_l(mMutex);
  82. ref_entry*ref=newref_entry;
  83. //Referencecountatthetimeofthesnapshot,butbeforethe
  84. //update.Positivevaluemeansweincrement,negative--we
  85. //decrementthereferencecount.
  86. ref->ref=mRef;
  87. ref->id=id;
  88. #ifDEBUG_REFS_CALLSTACK_ENABLED
  89. ref->stack.update(2);
  90. #endif
  91. ref->next=*refs;
  92. *refs=ref;
  93. }
  94. }
  95. voidremoveRef(ref_entry**refs,constvoid*id)
  96. {
  97. if(mTrackEnabled){
  98. AutoMutex_l(mMutex);
  99. ref_entry*ref=*refs;
  100. while(ref!=NULL){
  101. if(ref->id==id){
  102. *refs=ref->next;
  103. deleteref;
  104. return;
  105. }
  106. refs=&ref->next;
  107. ref=*refs;
  108. }
  109. LOG_ALWAYS_FATAL("RefBase:removingid%ponRefBase%p(weakref_type%p)thatdoesn'texist!",
  110. id,mBase,this);
  111. }
  112. }
  113. ......
  114. MutexmMutex;
  115. ref_entry*mStrongRefs;
  116. ref_entry*mWeakRefs;
  117. boolmTrackEnabled;
  118. //Collectstacktracesonaddrefandremoveref,insteadofdeletingthestackreferences
  119. //onremoverefthatmatchtheaddressones.
  120. boolmRetain;
  121. ......
  122. #endif
  123. };
这个类看起来实现得很复杂,其实不然,这个类的实现可以分成两部分:

  1. #if!DEBUG_REFS
  2. ......
  3. #else
编译指令之间的这部分源代码是Release版本的源代码,它的成员函数都是空实现;

  1. #else
  2. ......
  3. #endif
编译指令之间的部分源代码是Debug版本的源代码,它的成员函数都是有实现的,实现这些函数的目的都是为了方便开发人员调试引用计数用的,除此之外,还在内部实现了一个结构体ref_entry:

  1. structref_entry
  2. {
  3. ref_entry*next;
  4. constvoid*id;
  5. #ifDEBUG_REFS_CALLSTACK_ENABLED
  6. CallStackstack;
  7. #endif
  8. int32_tref;
  9. };
这个结构体也是为了方便调试而使用的,我们可以不关注这部分用于调试的代码。

总的来说,weakref_impl类只要提供了以下四个成员变量来维护对象的引用计数:

  1. volatileint32_tmStrong;
  2. volatileint32_tmWeak;
  3. RefBase*constmBase;
  4. volatileint32_tmFlags;
其中mStrong和mWeak分别表示对象的强引用计数和弱引用计数;RefBase类包含了一个weakref_impl类指针mRefs,而这里的weakref_impl类也有一个成员变量mBase来指向它的宿主类RefBase;mFlags是一个标志位,它指示了维护对象引用计数所使用的策略,后面我们将会分析到,它的取值为0,或者以下的枚举值:

  1. //!FlagsforextendObjectLifetime()
  2. enum{
  3. OBJECT_LIFETIME_WEAK=0x0001,
  4. OBJECT_LIFETIME_FOREVER=0x0003
  5. };
这里我们还需要注意的一点的是,从weakref_impl的类名来看,它应该是一个实现类,那么,就必然有一个对应的接口类,这个对应的接口类的就是RefBase类内部定义的weakref_type类了,这是一种把类的实现与接口定义分离的设计方法。学习过设计模式的读者应该知道,在设计模式里面,非常强调类的接口定义和类的实现分离,以便利于后续扩展和维护,这里就是用到了这种设计思想。

说了这多,RefBase类给人的感觉还是挺复杂的,不要紧,我们一步步来,先通过下面这个图来梳理一下这些类之间的关系:


从这个类图可以看出,每一个RefBase对象包含了一个weakref_impl对象,而weakref_impl对象实现了weakref_type接口,同时它可以包含多个ref_entry对象,前面说过,ref_entry是调试用的一个结构体,实际使用中可以不关注。

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

  1. template<typenameT>
  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. voidRefBase::incStrong(constvoid*id)const
  2. {
  3. weakref_impl*constrefs=mRefs;
  4. refs->addWeakRef(id);
  5. refs->incWeak(id);
  6. refs->addStrongRef(id);
  7. constint32_tc=android_atomic_inc(&refs->mStrong);
  8. LOG_ASSERT(c>0,"incStrong()calledon%pafterlaststrongref",refs);
  9. #ifPRINT_REFS
  10. LOGD("incStrongof%pfrom%p:cnt=%d\n",this,id,c);
  11. #endif
  12. if(c!=INITIAL_STRONG_VALUE){
  13. return;
  14. }
  15. android_atomic_add(-INITIAL_STRONG_VALUE,&refs->mStrong);
  16. const_cast<RefBase*>(this)->onFirstRef();
  17. }

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

  1. RefBase::RefBase()
  2. :mRefs(newweakref_impl(this))
  3. {
  4. //LOGV("Creatingrefs%pwithRefBase%p\n",mRefs,this);
  5. }
在这个incStrong函数中,主要做了三件事情:

一是增加弱引用计数:

  1. refs->addWeakRef(id);
  2. refs->incWeak(id);
二是增加强引用计数:
  1. refs->addStrongRef(id);
  2. constint32_tc=android_atomic_inc(&refs->mStrong);
三是如果发现是首次调用这个对象的incStrong函数,就会调用一个这个对象的onFirstRef函数,让对象有机会在对象被首次引用时做一些处理逻辑:
  1. if(c!=INITIAL_STRONG_VALUE){
  2. return;
  3. }
  4. android_atomic_add(-INITIAL_STRONG_VALUE,&refs->mStrong);
  5. const_cast<RefBase*>(this)->onFirstRef();
这里的c返回的是refs->mStrong加1前的值,如果发现等于INITIAL_STRONG_VALUE,就说明这个对象的强引用计数是第一次被增加,因此,refs->mStrong就是初始化为INITIAL_STRONG_VALUE的,它的值为:

  1. #defineINITIAL_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. voidRefBase::weakref_type::incWeak(constvoid*id)
  2. {
  3. weakref_impl*constimpl=static_cast<weakref_impl*>(this);
  4. impl->addWeakRef(id);
  5. constint32_tc=android_atomic_inc(&impl->mWeak);
  6. LOG_ASSERT(c>=0,"incWeakcalledon%pafterlastweakref",this);
  7. }
增加弱引用计数是下面语句执行的:
  1. constint32_tc=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<typenameT>
  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. voidRefBase::decStrong(constvoid*id)const
  2. {
  3. weakref_impl*constrefs=mRefs;
  4. refs->removeStrongRef(id);
  5. constint32_tc=android_atomic_dec(&refs->mStrong);
  6. #ifPRINT_REFS
  7. LOGD("decStrongof%pfrom%p:cnt=%d\n",this,id,c);
  8. #endif
  9. LOG_ASSERT(c>=1,"decStrong()calledon%ptoomanytimes",refs);
  10. if(c==1){
  11. const_cast<RefBase*>(this)->onLastStrongRef(id);
  12. if((refs->mFlags&OBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK){
  13. deletethis;
  14. }
  15. }
  16. refs->removeWeakRef(id);
  17. refs->decWeak(id);
  18. }
这里的refs->removeStrongRef函数调用语句是对应前面在RefBase::incStrong函数里的refs->addStrongRef函数调用语句的,在Release版本中,这也是一个空实现函数,真正实现强引用计数减1的操作是下面语句:
  1. constint32_tc=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. deletethis;
  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. voidRefBase::weakref_type::decWeak(constvoid*id)
  2. {
  3. weakref_impl*constimpl=static_cast<weakref_impl*>(this);
  4. impl->removeWeakRef(id);
  5. constint32_tc=android_atomic_dec(&impl->mWeak);
  6. LOG_ASSERT(c>=1,"decWeakcalledon%ptoomanytimes",this);
  7. if(c!=1)return;
  8. if((impl->mFlags&OBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK){
  9. if(impl->mStrong==INITIAL_STRONG_VALUE)
  10. deleteimpl->mBase;
  11. else{
  12. //LOGV("Freeingrefs%pofoldRefBase%p\n",this,impl->mBase);
  13. deleteimpl;
  14. }
  15. }else{
  16. impl->mBase->onLastWeakRef(id);
  17. if((impl->mFlags&OBJECT_LIFETIME_FOREVER)!=OBJECT_LIFETIME_FOREVER){
  18. deleteimpl->mBase;
  19. }
  20. }
  21. }
这里又一次调用了weakref_impl对象的removeWeakRef函数,这也是和RefBase::weakref_type::incWeak函数里面的impl->addWeakRef语句所对应的,实现弱引用计数减1的操作是下面语句:

  1. constint32_tc=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. deleteimpl->mBase;
  4. else{
  5. //LOGV("Freeingrefs%pofoldRefBase%p\n",this,impl->mBase);
  6. deleteimpl;
  7. }
  8. }else{
  9. impl->mBase->onLastWeakRef(id);
  10. if((impl->mFlags&OBJECT_LIFETIME_FOREVER)!=OBJECT_LIFETIME_FOREVER){
  11. deleteimpl->mBase;
  12. }
  13. }
如果目标对象的生命周期是不受弱引用计数控制的,就执行下面语句:

  1. if(impl->mStrong==INITIAL_STRONG_VALUE)
  2. deleteimpl->mBase;
  3. else{
  4. //LOGV("Freeingrefs%pofoldRefBase%p\n",this,impl->mBase);
  5. deleteimpl;
  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("DestroyingRefBase%p(refs%p)\n",this,mRefs);
  4. if(mRefs->mWeak==0){
  5. //LOGV("Freeingrefs%pofoldRefBase%p\n",mRefs,this);
  6. deletemRefs;
  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掉了。

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

  1. impl->mBase->onLastWeakRef(id);
  2. if((impl->mFlags&OBJECT_LIFETIME_FOREVER)!=OBJECT_LIFETIME_FOREVER){
  3. deleteimpl->mBase;
  4. }

理论上说,如果目标对象的生命周期是受弱引用计数控制的,那么当强引用计数和弱引用计数都为0的时候,这时候就应该delete目标对象了,但是这里还有另外一层控制,我们可以设置目标对象的标志值为OBJECT_LIFETIME_FOREVER,即目标对象的生命周期完全不受强引用计数和弱引用计数控制,在这种情况下,即使目标对象的强引用计数和弱引用计数都同时为0,这里也不能delete这个目标对象,那么,由谁来delete掉呢?当然是谁new出来的,就谁来delete掉了,这时候智能指针就完全退化为普通指针了,这里的智能指针设计的非常强大。


分析到这里,有必要小结一下:

A. 如果对象的标志位被设置为0,那么只要发现对象的强引用计数值为0,那就会自动delete掉这个对象;

B. 如果对象的标志位被设置为OBJECT_LIFETIME_WEAK,那么只有当对象的强引用计数和弱引用计数都为0的时候,才会自动delete掉这个对象;

C. 如果对象的标志位被设置为OBJECT_LIFETIME_FOREVER,那么对象就永远不会自动被delete掉,谁new出来的对象谁来delete掉。

到了这里,强指针就分析完成了,最后来分析弱指针。


4. 弱指针

弱指针所使用的引用计数类与强指针一样,都是RefBase类,因此,这里就不再重复介绍了,我们直接来弱指针的实现,它定义在frameworks/base/include/utils/RefBase.h文件中:

  1. template<typenameT>
  2. classwp
  3. {
  4. public:
  5. typedeftypenameRefBase::weakref_typeweakref_type;
  6. inlinewp():m_ptr(0){}
  7. wp(T*other);
  8. wp(constwp<T>&other);
  9. wp(constsp<T>&other);
  10. template<typenameU>wp(U*other);
  11. template<typenameU>wp(constsp<U>&other);
  12. template<typenameU>wp(constwp<U>&other);
  13. ~wp();
  14. //Assignment
  15. wp&operator=(T*other);
  16. wp&operator=(constwp<T>&other);
  17. wp&operator=(constsp<T>&other);
  18. template<typenameU>wp&operator=(U*other);
  19. template<typenameU>wp&operator=(constwp<U>&other);
  20. template<typenameU>wp&operator=(constsp<U>&other);
  21. voidset_object_and_refs(T*other,weakref_type*refs);
  22. //promotiontosp
  23. sp<T>promote()const;
  24. //Reset
  25. voidclear();
  26. //Accessors
  27. inlineweakref_type*get_refs()const{returnm_refs;}
  28. inlineT*unsafe_get()const{returnm_ptr;}
  29. //Operators
  30. COMPARE_WEAK(==)
  31. COMPARE_WEAK(!=)
  32. COMPARE_WEAK(>)
  33. COMPARE_WEAK(<)
  34. COMPARE_WEAK(<=)
  35. COMPARE_WEAK(>=)
  36. inlinebooloperator==(constwp<T>&o)const{
  37. return(m_ptr==o.m_ptr)&&(m_refs==o.m_refs);
  38. }
  39. template<typenameU>
  40. inlinebooloperator==(constwp<U>&o)const{
  41. returnm_ptr==o.m_ptr;
  42. }
  43. inlinebooloperator>(constwp<T>&o)const{
  44. return(m_ptr==o.m_ptr)?(m_refs>o.m_refs):(m_ptr>o.m_ptr);
  45. }
  46. template<typenameU>
  47. inlinebooloperator>(constwp<U>&o)const{
  48. return(m_ptr==o.m_ptr)?(m_refs>o.m_refs):(m_ptr>o.m_ptr);
  49. }
  50. inlinebooloperator<(constwp<T>&o)const{
  51. return(m_ptr==o.m_ptr)?(m_refs<o.m_refs):(m_ptr<o.m_ptr);
  52. }
  53. template<typenameU>
  54. inlinebooloperator<(constwp<U>&o)const{
  55. return(m_ptr==o.m_ptr)?(m_refs<o.m_refs):(m_ptr<o.m_ptr);
  56. }
  57. inlinebooloperator!=(constwp<T>&o)const{returnm_refs!=o.m_refs;}
  58. template<typenameU>inlinebooloperator!=(constwp<U>&o)const{return!operator==(o);}
  59. inlinebooloperator<=(constwp<T>&o)const{return!operator>(o);}
  60. template<typenameU>inlinebooloperator<=(constwp<U>&o)const{return!operator>(o);}
  61. inlinebooloperator>=(constwp<T>&o)const{return!operator<(o);}
  62. template<typenameU>inlinebooloperator>=(constwp<U>&o)const{return!operator<(o);}
  63. private:
  64. template<typenameY>friendclasssp;
  65. template<typenameY>friendclasswp;
  66. T*m_ptr;
  67. weakref_type*m_refs;
  68. };
与强指针类相比,它们都有一个成员变量m_ptr指向目标对象,但是弱指针还有一个额外的成员变量m_refs,它的类型是weakref_type指针,下面我们分析弱指针的构造函数时再看看它是如果初始化的。这里我们需要关注的仍然是弱指针的构造函数和析构函数。

先来看构造函数:

  1. template<typenameT>
  2. wp<T>::wp(T*other)
  3. :m_ptr(other)
  4. {
  5. if(other)m_refs=other->createWeak(this);
  6. }
这里的参数other一定是继承了RefBase类,因此,这里调用了RefBase类的createWeak函数,它定义在frameworks/base/libs/utils/RefBase.cpp文件中:

  1. RefBase::weakref_type*RefBase::createWeak(constvoid*id)const
  2. {
  3. mRefs->incWeak(id);
  4. returnmRefs;
  5. }
这里的成员变量mRefs的类型为weakref_impl指针,weakref_impl类的incWeak函数我们在前面已经看过了,它的作用就是增加对象的弱引用计数。函数最后返回mRefs,于是,弱指针对象的成员变量m_refs就指向目标对象的weakref_impl对象了。

再来看析构函数:

  1. template<typenameT>
  2. wp<T>::~wp()
  3. {
  4. if(m_ptr)m_refs->decWeak(this);
  5. }
这里,弱指针在析构的时候,与强指针析构不一样,它直接就调用目标对象的weakref_impl对象的decWeak函数来减少弱引用计数了,当弱引用计数为0的时候,就会根据在目标对象的标志位(0、OBJECT_LIFETIME_WEAK或者OBJECT_LIFETIME_FOREVER)来决定是否要delete目标对象,前面我们已经介绍过了,这里就不再介绍了。

分析到这里,弱指针还没介绍完,它最重要的特性我们还没有分析到。前面我们说过,弱指针的最大特点是它不能直接操作目标对象,这是怎么样做到的呢?秘密就在于弱指针类没有重载*和->操作符号,而强指针重载了这两个操作符号。但是,如果我们要操作目标对象,应该怎么办呢,这就要把弱指针升级为强指针了:

  1. template<typenameT>
  2. sp<T>wp<T>::promote()const
  3. {
  4. returnsp<T>(m_ptr,m_refs);
  5. }
升级的方式就使用成员变量m_ptr和m_refs来构造一个强指针sp,这里的m_ptr为指目标对象的一个指针,而m_refs则是指向目标对象里面的weakref_impl对象。

我们再来看看这个强指针的构造过程:

  1. template<typenameT>
  2. sp<T>::sp(T*p,weakref_type*refs)
  3. :m_ptr((p&&refs->attemptIncStrong(this))?p:0)
  4. {
  5. }
主要就是初始化指向目标对象的成员变量m_ptr了,如果目标对象还存在,这个m_ptr就指向目标对象,如果目标对象已经不存在,m_ptr就为NULL,升级成功与否就要看refs->attemptIncStrong函数的返回结果了:
  1. boolRefBase::weakref_type::attemptIncStrong(constvoid*id)
  2. {
  3. incWeak(id);
  4. weakref_impl*constimpl=static_cast<weakref_impl*>(this);
  5. int32_tcurCount=impl->mStrong;
  6. LOG_ASSERT(curCount>=0,"attemptIncStrongcalledon%pafterunderflow",
  7. this);
  8. while(curCount>0&&curCount!=INITIAL_STRONG_VALUE){
  9. if(android_atomic_cmpxchg(curCount,curCount+1,&impl->mStrong)==0){
  10. break;
  11. }
  12. curCount=impl->mStrong;
  13. }
  14. if(curCount<=0||curCount==INITIAL_STRONG_VALUE){
  15. boolallow;
  16. if(curCount==INITIAL_STRONG_VALUE){
  17. //Attemptingtoacquirefirststrongreference...thisisallowed
  18. //iftheobjectdoesNOThavealongerlifetime(meaningthe
  19. //implementationdoesn'tneedtoseethis),oriftheimplementation
  20. //allowsittohappen.
  21. allow=(impl->mFlags&OBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK
  22. ||impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id);
  23. }else{
  24. //Attemptingtorevivetheobject...thisisallowed
  25. //iftheobjectDOEShavealongerlifetime(sowecansafely
  26. //calltheobjectwithonlyaweakref)andtheimplementation
  27. //allowsittohappen.
  28. allow=(impl->mFlags&OBJECT_LIFETIME_WEAK)==OBJECT_LIFETIME_WEAK
  29. &&impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id);
  30. }
  31. if(!allow){
  32. decWeak(id);
  33. returnfalse;
  34. }
  35. curCount=android_atomic_inc(&impl->mStrong);
  36. //Ifthestrongreferencecounthasalreadybeenincrementedby
  37. //someoneelse,theimplementorofonIncStrongAttempted()isholding
  38. //anunneededreference.SocallonLastStrongRef()heretoremoveit.
  39. //(No,thisisnotpretty.)NotethatweMUSTNOTdothisifwe
  40. //areinfactacquiringthefirstreference.
  41. if(curCount>0&&curCount<INITIAL_STRONG_VALUE){
  42. impl->mBase->onLastStrongRef(id);
  43. }
  44. }
  45. impl->addWeakRef(id);
  46. impl->addStrongRef(id);
  47. #ifPRINT_REFS
  48. LOGD("attemptIncStrongof%pfrom%p:cnt=%d\n",this,id,curCount);
  49. #endif
  50. if(curCount==INITIAL_STRONG_VALUE){
  51. android_atomic_add(-INITIAL_STRONG_VALUE,&impl->mStrong);
  52. impl->mBase->onFirstRef();
  53. }
  54. returntrue;
  55. }

这个函数的作用是试图增加目标对象的强引用计数,但是有可能会失败,失败的原因可能是因为目标对象已经被delete掉了,或者是其它的原因,下面会分析到。前面我们在讨论强指针的时候说到,增加目标对象的强引用计数的同时,也会增加目标对象的弱引用计数,因此,函数在开始的地方首先就是调用incWeak函数来先增加目标对象的引用计数,如果后面试图增加目标对象的强引用计数失败时,会调用decWeak函数来回滚前面的incWeak操作。

这里试图增加目标对象的强引用计数时,分两种情况讨论,一种情况是此时目标对象正在被其它强指针引用,即它的强引用计数大于0,并且不等于INITIAL_STRONG_VALUE,另一种情况是此时目标对象没有被任何强指针引用,即它的强引用计数小于等于0,或者等于INITIAL_STRONG_VALUE。

第一种情况比较简单,因为这时候说明目标对象一定存在,因此,是可以将这个弱指针提升为强指针的,在这种情况下,只要简单地增加目标对象的强引用计数值就行了:

  1. while(curCount>0&&curCount!=INITIAL_STRONG_VALUE){
  2. if(android_atomic_cmpxchg(curCount,curCount+1,&impl->mStrong)==0){
  3. break;
  4. }
  5. curCount=impl->mStrong;
  6. }
当我们在这里对目标对象的强引用计数执行加1操作时,要保证原子性,因为其它地方也有可能正在对这个目标对象的强引用计数执行加1的操作,前面我们一般是调用android_atomic_inc函数来完成,但是这里是通过调用android_atomic_cmpxchg函数来完成,android_atomic_cmpxchg函数是体系结构相关的函数,在提供了一些特殊的指令的体系结构上,调用android_atomic_cmpxchg函数来执行加1操作的效率会比调用android_atomic_inc函数更高一些。函数android_atomic_cmpxchg是在system/core/include/cutils/atomic.h文件中定义的一个宏:

  1. intandroid_atomic_release_cas(int32_toldvalue,int32_tnewvalue,
  2. volatileint32_t*addr);
  3. #defineandroid_atomic_cmpxchgandroid_atomic_release_cas
它实际执行的函数是android_atomic_release_cas,这个函数的工作原理大概是这样的:如果它发现*addr == oldvalue,就会执行*addr = newvalue的操作,然后返回0,否则什么也不做,返回1。在我们讨论的这个场景中,oldvalue等于curCount,而newvalue等于curCount + 1,于是,在*addr == oldvalue的条件下,就相当于是对目标对象的强引用计数值增加了1。什么情况下*addr != oldvalue呢?在调用android_atomic_release_cas函数之前,oldvalue和值就是从地址addr读出来的,如果在执行android_atomic_release_cas函数的时候,有其它地方也对地址addr进行操作,那么就会有可能出现*addr != oldvalue的情况,这时候就说明其它地方也在操作目标对象的强引用计数了,因此,这里就不能执行增加目标对象的强引用计数的操作了,它必须要等到其它地方操作完目标对象的强引用计数之后再重新执行,这就是为什么要通过一个while循环来执行了。

第二种情况比较复杂一点,因为这时候目标对象可能还存在,也可能不存了,这要根据实际情况来判断。如果此时目标对象的强引用计数值等于INITIAL_STRONG_VALUE,说明此目标对象还从未被强指针引用过,这时候弱指针能够被提升为强指针的条件就为:

  1. allow=(impl->mFlags&OBJECT_LIFETIME_WEAK)!=OBJECT_LIFETIME_WEAK
  2. ||impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id);

即如果目标对象的生命周期只受到强引用计数控制或者在目标对象的具体实现中总是允许这种情况发生。怎么理解呢?如果目标对象的生命周期只受强引用计数控制(它的标志位mFlags为0),而这时目标对象又还未被强指针引用过,它自然就不会被delete掉,因此,这时候可以判断出目标对象是存在的;如果目标对象的生命周期受弱引用计数控制(OBJECT_LIFETIME_WEAK),这时候由于目标对象正在被弱指针引用,因此,弱引用计数一定不为0,目标对象一定存在;如果目标对象的生命周期不受引用计数控制(BJECT_LIFETIME_FOREVER),这时候目标对象也是下在被弱指针引用,因此,目标对象的所有者必须保证这个目标对象还没有被delete掉,否则就会出问题了。在后面两种场景下,因为目标对象的生命周期都是不受强引用计数控制的,而现在又要把弱指针提升为强指针,就需要进一步调用目标对象的onIncStrongAttempted来看看是否允许这种情况发生,这又该怎么理解呢?可以这样理解,目标对象的设计者可能本身就不希望这个对象被强指针引用,只能通过弱指针来引用它,因此,这里它就可以重载其父类的onIncStrongAttempted函数,然后返回false,这样就可以阻止弱指针都被提升为强指针。在RefBase类中,其成员函数onIncStrongAttempted默认是返回true的:

  1. boolRefBase::onIncStrongAttempted(uint32_tflags,constvoid*id)
  2. {
  3. return(flags&FIRST_INC_STRONG)?true:false;
  4. }

如果此时目标对象的强引用计数值小于等于0,那就说明该对象之前一定被强指针引用过,这时候就必须保证目标对象是被弱引用计数控制的(BJECT_LIFETIME_WEAK),否则的话,目标对象就已经被delete了。同样,这里也要调用一下目标对象的onIncStrongAttempted成员函数,来询问一下目标对象在强引用计数值小于等于0的时候,是否允计将弱指针提升为强指针。下面这个代码段就是执行上面所说的逻辑:

  1. allow=(impl->mFlags&OBJECT_LIFETIME_WEAK)==OBJECT_LIFETIME_WEAK
  2. &&impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id);
继续往下看:

  1. if(!allow){
  2. decWeak(id);
  3. returnfalse;
  4. }
  5. curCount=android_atomic_inc(&impl->mStrong);
如果allow值为false,那么就说明不允计把这个弱指针提升为强指针,因此就返回false了,在返回之前,要先调用decWeak函数来减少目标对象的弱引用计数,因为函数的开头不管三七二十一,首先就调用了incWeak来增加目标对象的弱引用计数值。

函数attemptIncStrong的主体逻辑大概就是这样了,比较复杂,读者要细细体会一下。函数的最后,如果此弱指针是允计提升为强指针的,并且此目标对象是第一次被强指针引用,还需要调整一下目标对象的强引用计数值:

  1. if(curCount==INITIAL_STRONG_VALUE){
  2. android_atomic_add(-INITIAL_STRONG_VALUE,&impl->mStrong);
  3. impl->mBase->onFirstRef();
  4. }
这个逻辑我们在前面分析强指针时已经分析过了,这里不再详述。

分析到这里,弱指针就介绍完了。强指针和弱指针的关系比较密切,同时它们也比较复杂,下面我们再举一个例子来说明强指针和弱指针的用法,同时也验证一下它们的实现原理。


5. 强指针和弱指针的用法

参考在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序一文,我们在external目录下建立一个C++工程目录weightpointer,它里面有两个文件,一个weightpointer.cpp文件,另外一个是Android.mk文件。

源文件weightpointer.cpp的内容如下:

  1. #include<stdio.h>
  2. #include<utils/RefBase.h>
  3. #defineINITIAL_STRONG_VALUE(1<<28)
  4. usingnamespaceandroid;
  5. classWeightClass:publicRefBase
  6. {
  7. public:
  8. voidprintRefCount()
  9. {
  10. int32_tstrong=getStrongCount();
  11. weakref_type*ref=getWeakRefs();
  12. printf("-----------------------\n");
  13. printf("StrongRefCount:%d.\n",(strong==INITIAL_STRONG_VALUE?0:strong));
  14. printf("WeakRefCount:%d.\n",ref->getWeakCount());
  15. printf("-----------------------\n");
  16. }
  17. };
  18. classStrongClass:publicWeightClass
  19. {
  20. public:
  21. StrongClass()
  22. {
  23. printf("ConstructStrongClassObject.\n");
  24. }
  25. virtual~StrongClass()
  26. {
  27. printf("DestoryStrongClassObject.\n");
  28. }
  29. };
  30. classWeakClass:publicWeightClass
  31. {
  32. public:
  33. WeakClass()
  34. {
  35. extendObjectLifetime(OBJECT_LIFETIME_WEAK);
  36. printf("ConstructWeakClassObject.\n");
  37. }
  38. virtual~WeakClass()
  39. {
  40. printf("DestoryWeakClassObject.\n");
  41. }
  42. };
  43. classForeverClass:publicWeightClass
  44. {
  45. public:
  46. ForeverClass()
  47. {
  48. extendObjectLifetime(OBJECT_LIFETIME_FOREVER);
  49. printf("ConstructForeverClassObject.\n");
  50. }
  51. virtual~ForeverClass()
  52. {
  53. printf("DestoryForeverClassObject.\n");
  54. }
  55. };
  56. voidTestStrongClass(StrongClass*pStrongClass)
  57. {
  58. wp<StrongClass>wpOut=pStrongClass;
  59. pStrongClass->printRefCount();
  60. {
  61. sp<StrongClass>spInner=pStrongClass;
  62. pStrongClass->printRefCount();
  63. }
  64. sp<StrongClass>spOut=wpOut.promote();
  65. printf("spOut:%p.\n",spOut.get());
  66. }
  67. voidTestWeakClass(WeakClass*pWeakClass)
  68. {
  69. wp<WeakClass>wpOut=pWeakClass;
  70. pWeakClass->printRefCount();
  71. {
  72. sp<WeakClass>spInner=pWeakClass;
  73. pWeakClass->printRefCount();
  74. }
  75. pWeakClass->printRefCount();
  76. sp<WeakClass>spOut=wpOut.promote();
  77. printf("spOut:%p.\n",spOut.get());
  78. }
  79. voidTestForeverClass(ForeverClass*pForeverClass)
  80. {
  81. wp<ForeverClass>wpOut=pForeverClass;
  82. pForeverClass->printRefCount();
  83. {
  84. sp<ForeverClass>spInner=pForeverClass;
  85. pForeverClass->printRefCount();
  86. }
  87. }
  88. intmain(intargc,char**argv)
  89. {
  90. printf("TestStrongClass:\n");
  91. StrongClass*pStrongClass=newStrongClass();
  92. TestStrongClass(pStrongClass);
  93. printf("\nTestWeakClass:\n");
  94. WeakClass*pWeakClass=newWeakClass();
  95. TestWeakClass(pWeakClass);
  96. printf("\nTestFroeverClass:\n");
  97. ForeverClass*pForeverClass=newForeverClass();
  98. TestForeverClass(pForeverClass);
  99. pForeverClass->printRefCount();
  100. deletepForeverClass;
  101. return0;
  102. }

首先定义了一个基类WeightClass,继承于RefBase类,它只有一个成员函数printRefCount,作用是用来输出引用计数。接着分别定义了三个类StrongClass、WeakClass和ForeverClass,其中实例化StrongClass类的得到的对象的标志位为默认值0,实例化WeakClass类的得到的对象的标志位为OBJECT_LIFETIME_WEAK,实例化ForeverClass类的得到的对象的标志位为OBJECT_LIFETIME_FOREVER,后两者都是通过调用RefBase类的extendObjectLifetime成员函数来设置的。

在main函数里面,分别实例化了这三个类的对象出来,然后分别传给TestStrongClass函数、TestWeakClass函数和TestForeverClass函数来说明智能指针的用法,我们主要是通过考察它们的强引用计数和弱引用计数来验证智能指针的实现原理。

编译脚本文件Android.mk的内容如下:

  1. LOCAL_PATH:=$(callmy-dir)
  2. include$(CLEAR_VARS)
  3. LOCAL_MODULE_TAGS:=optional
  4. LOCAL_MODULE:=weightpointer
  5. LOCAL_SRC_FILES:=weightpointer.cpp
  6. LOCAL_SHARED_LIBRARIES:=\
  7. libcutils\
  8. libutils
  9. include$(BUILD_EXECUTABLE)
最后,我们参照如何单独编译Android源代码中的模块一文,使用mmm命令对工程进行编译:
  1. USER-NAME@MACHINE-NAME:~/Android$mmm./external/weightpointer
编译之后,就可以打包了:

  1. USER-NAME@MACHINE-NAME:~/Android$makesnod
最后得到可执行程序weightpointer就位于设备上的/system/bin/目录下。启动模拟器,通过adb shell命令进入到模拟器终端,进入到/system/bin/目录,执行weightpointer可执行程序,验证程序是否按照我们设计的逻辑运行:
  1. USER-NAME@MACHINE-NAME:~/Android$adbshell
  2. root@android:/#cdsystem/bin/
  3. root@android:/system/bin#./weightpointer
执行TestStrongClass函数的输出为:

  1. TestStrongClass:
  2. ConstructStrongClassObject.
  3. -----------------------
  4. StrongRefCount:0.
  5. WeakRefCount:1.
  6. -----------------------
  7. -----------------------
  8. StrongRefCount:1.
  9. WeakRefCount:2.
  10. -----------------------
  11. DestoryStrongClassObject.
  12. 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函数的输出为:

  1. TestWeakClass:
  2. ConstructWeakClassObject.
  3. -----------------------
  4. StrongRefCount:0.
  5. WeakRefCount:1.
  6. -----------------------
  7. -----------------------
  8. StrongRefCount:1.
  9. WeakRefCount:2.
  10. -----------------------
  11. -----------------------
  12. StrongRefCount:0.
  13. WeakRefCount:1.
  14. -----------------------
  15. spOut:0xa528.
  16. DestoryWeakClassObject.

TestWeakClass函数和TestStrongClass函数的执行过程基本一样,所不同的是当程序跳出大括号之后,虽然这个WeakClass对象的强引用计数值已经为0,但是由于它的生命周期同时受强引用计数和弱引用计数控制,而这时它的弱引用计数值大于0,因此,这个WeakClass对象不会被delete掉,这一点可以从后面试图把弱批针wpOut提升为强指针时得到的对象指针不为0得到验证。

执行TestForeverClass函数的输出来:

  1. TestFroeverClass:
  2. ConstructForeverClassObject.
  3. -----------------------
  4. StrongRefCount:0.
  5. WeakRefCount:1.
  6. -----------------------
  7. -----------------------
  8. StrongRefCount:1.
  9. WeakRefCount:2.
  10. -----------------------
当执行完TestForeverClass函数返回到main函数的输出来:

  1. -----------------------
  2. StrongRefCount:0.
  3. WeakRefCount:0.
  4. -----------------------
  5. DestoryForeverClassObject.
这里我们可以看出,虽然这个ForeverClass对象的强引用计数和弱引用计数值均为0了,但是它不自动被delete掉,虽然由我们手动地delete这个对象,它才会被析构,这是因为这个ForeverClass对象的生命周期是既不受强引用计数值控制,也不会弱引用计数值控制。

这样,从TestStrongClass、TestWeakClass和TestForeverClass这三个函数的输出就可以验证了我们上面对Android系统的强指针和弱指针的实现原理的分析。

至此,Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理就分析完成了,它实现得很小巧但是很精致,希望读者可以通过实际操作细细体会一下。



转载声明: 本文转自Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析


参考推荐:

C++ 智能指针

Java 内存模型及GC原理

老罗的Android之旅专注于Android底层模块分析,强烈推荐



分享到:
评论

相关推荐

    欧姆龙NJ PLC与多品牌总线设备控制程序详解及应用实例

    内容概要:本文详细介绍了欧姆龙NJ系列PLC与多个品牌总线设备(如汇川伺服、雷赛步进控制器、SMC电缸等)的控制程序及其配置方法。重点讨论了PDO映射、参数配置、单位转换、故障排查等方面的实际经验和常见问题。文中提供了具体的代码示例,帮助读者理解和掌握这些复杂系统的调试技巧。此外,还特别强调了不同品牌设备之间的兼容性和注意事项,以及如何避免常见的配置错误。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些需要进行PLC与总线设备集成工作的专业人士。 使用场景及目标:适用于需要将欧姆龙NJ PLC与其他品牌总线设备集成在一起的应用场景,如工厂自动化生产线、机器人控制等。主要目标是提高系统的可靠性和效率,减少调试时间和成本。 其他说明:文章不仅提供了理论知识,还包括大量来自实际项目的实践经验,有助于读者更好地应对现实中的挑战。建议读者在实践中不断积累经验,逐步掌握各种设备的特点和最佳实践。

    数字化企业转型大数据解决方案.pptx

    数字化企业转型大数据解决方案.pptx

    基于MATLAB的多智能体一致性算法在电力系统分布式经济调度中的应用

    内容概要:本文详细介绍了利用MATLAB实现多智能体系统一致性算法在电力系统分布式经济调度中的应用。文中通过具体的MATLAB代码展示了如何将发电机组和柔性负荷视为智能体,通过局部通信和协商达成全局最优调度。核心算法通过迭代更新增量成本和增量效益,使各个节点在无中央指挥的情况下自行调整功率,最终实现经济最优分配。此外,文章还讨论了通信拓扑对收敛速度的影响以及一些工程优化技巧,如稀疏矩阵存储和自适应参数调整。 适合人群:从事电力系统调度、分布式控制系统设计的研究人员和技术人员,尤其是对多智能体系统和MATLAB编程有一定了解的人群。 使用场景及目标:适用于希望提高电力系统调度效率、降低成本并增强系统鲁棒性的应用场景。主要目标是在分布式环境下实现快速、稳定的经济调度,同时减少通信量和计算资源消耗。 其他说明:文章提供了详细的代码示例和测试结果,展示了算法的实际性能和优势。对于进一步研究和实际应用具有重要参考价值。

    获取虎牙直播流地址的油猴脚本,可以直接使用VLC等播放器打开地址播放

    获取虎牙直播流地址的油猴脚本,可以直接使用VLC等播放器打开地址播放。

    电力系统中基于MATLAB的价格型需求响应与电价弹性矩阵优化

    内容概要:本文详细介绍了如何利用MATLAB进行价格型需求响应的研究,特别是电价弹性矩阵的构建与优化。文章首先解释了电价弹性矩阵的概念及其重要性,接着展示了如何通过MATLAB代码实现弹性矩阵的初始化、负荷变化量的计算以及优化方法。文中还讨论了如何通过非线性约束和目标函数最小化峰谷差,确保用户用电舒适度的同时实现负荷的有效调节。此外,文章提供了具体的代码实例,包括原始负荷曲线与优化后负荷曲线的对比图,以及基于历史数据的参数优化方法。 适合人群:从事电力系统优化、能源管理及相关领域的研究人员和技术人员。 使用场景及目标:适用于希望深入了解并掌握价格型需求响应机制的专业人士,旨在帮助他们更好地理解和应用电价弹性矩阵,优化电力系统的负荷分布,提高能源利用效率。 其他说明:文章强调了实际应用中的注意事项,如弹性矩阵的动态校准和用户价格敏感度的滞后效应,提供了实用的技术细节和实践经验。

    CSP-J 2021 初赛真题.pdf

    CSP-J 2021 初赛真题.pdf

    基于麻雀优化算法SSA与LSTM结合的MATLAB时间序列单输入单输出预测模型

    内容概要:本文详细介绍了如何利用麻雀优化算法(SSA)与长短期记忆网络(LSTM)相结合,在MATLAB环境中构建一个用于时间序列单输入单输出预测的模型。首先简述了SSA和LSTM的基本原理,接着逐步讲解了从数据准备、预处理、模型构建、参数优化到最后的预测与结果可视化的完整流程。文中提供了详细的MATLAB代码示例,确保读者能够轻松复现实验。此外,还讨论了一些关键参数的选择方法及其对模型性能的影响。 适合人群:对时间序列预测感兴趣的科研人员、研究生以及有一定编程基础的数据分析师。 使用场景及目标:适用于需要对单变量时间序列数据进行高精度预测的应用场合,如金融、能源等领域。通过本篇文章的学习,读者将掌握如何使用MATLAB实现SSA优化LSTM模型的具体步骤和技术要点。 其他说明:为了提高模型的泛化能力,文中特别强调了数据预处理的重要性,并给出了具体的实现方式。同时,针对可能出现的问题,如过拟合、梯度爆炸等,也提供了一些建议性的解决方案。

    西门子S7-1200 PLC与施耐德变频器Modbus通讯实现及调试技巧

    内容概要:本文详细介绍了西门子S7-1200 PLC与施耐德ATV310/312变频器通过Modbus RTU进行通讯的具体实现步骤和调试技巧。主要内容涵盖硬件接线、通讯参数配置、控制启停、设定频率、读取运行参数的方法以及常见的调试问题及其解决方案。文中提供了具体的代码示例,帮助读者理解和实施通讯程序。此外,还强调了注意事项,如地址偏移量、数据格式转换和超时匹配等。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些需要将西门子PLC与施耐德变频器进行集成的工作人员。 使用场景及目标:适用于需要通过Modbus RTU协议实现PLC与变频器通讯的工程项目。目标是确保通讯稳定可靠,掌握解决常见问题的方法,提高调试效率。 其他说明:文中提到的实际案例和调试经验有助于读者避免常见错误,快速定位并解决问题。建议读者在实践中结合提供的代码示例和调试工具进行操作。

    Scala语言思维导图

    本文详细介绍了Scala语言的基础知识和特性。Scala是一种运行在JVM上的编程语言,兼具面向对象和函数式编程的特点,适合大数据处理。其环境配置需注意Java版本和路径问题。语言基础涵盖注释、变量、数据类型、运算符和流程控制。函数特性包括高阶函数、柯里化、闭包、尾递归等。面向对象方面,Scala支持继承、抽象类、特质等,并通过包、类和对象实现代码组织和管理,同时提供了单例对象和伴生对象的概念。

    Comsol仿真探索石墨烯-金属强耦合拉比分裂现象及其应用

    内容概要:本文详细探讨了石墨烯-金属强耦合拉比分裂现象的研究,主要借助Comsol多物理场仿真软件进行模拟。文章首先介绍了拉比分裂的基本概念,即当石墨烯与金属相互靠近时,原本单一的共振模式会分裂成两个,这种现象背后的电磁学和量子力学原理对于开发新型光电器件、高速通信设备等意义重大。接着阐述了Comsol在研究中的重要作用,包括构建石墨烯-金属相互作用模型、设置材料属性、定义边界条件、划分网格以及求解模型的具体步骤。此外,还展示了具体的建模示例代码,并对模拟结果进行了深入分析,解释了拉比分裂现象的形成机理。最后强调了该研究对未来技术创新的重要价值。 适合人群:从事物理学、材料科学、光电工程等领域研究的专业人士,尤其是对石墨烯-金属强耦合感兴趣的科研工作者。 使用场景及目标:适用于希望深入了解石墨烯-金属强耦合拉比分裂现象的研究人员,旨在帮助他们掌握Comsol仿真工具的应用技巧,提高研究效率,推动相关领域的创新发展。 其他说明:文中提供的代码片段和建模思路可供读者参考实践,但需要注意实际应用时需根据具体情况调整参数配置。

    嵌入式电机控制中FPGA与Nios II结合的Verilog实现及优化技巧

    内容概要:本文详细介绍了基于FPGA的电机控制系统的设计与实现,重点探讨了Verilog和Nios II软核相结合的方式。具体来说,编码器模块利用Verilog实现了高精度的四倍频计数,解决了AB相信号的跳变问题;坐标变换部分则由Nios II软核负责,通过C语言实现Clarke变换和Park变换,提高了计算效率;SVPWM生成模块采用了Verilog硬件加速,优化了调制波的生成时间和波形质量。此外,文章还讨论了Nios II和Verilog之间的高效交互方式,如自定义指令和DMA传输,以及中断处理机制,确保系统的实时性和稳定性。文中提到的一些优化技巧,如定点数运算、查表法、流水线设计等,进一步提升了系统的性能。 适合人群:具有一定FPGA和嵌入式开发经验的研发人员,尤其是对电机控制感兴趣的工程师。 使用场景及目标:适用于需要高性能、低延迟的电机控制应用场景,如工业自动化、机器人、无人机等领域。目标是帮助读者掌握FPGA与Nios II结合的电机控制方法,提高系统的实时性和可靠性。 其他说明:文章提供了详细的代码片段和优化建议,有助于读者理解和实践。同时,文中提及了一些常见的调试问题及其解决方案,如符号位处理不当导致的电机反转、数据溢出等问题,提醒读者在实际项目中加以注意。

    ### 【嵌入式开发】基于Qt的ATK-DLRK3568实战指南:从入门到项目实战题:嵌

    内容概要:本文档《ATK-DLRK3568嵌入式Qt开发实战V1.2》是正点原子出品的一份面向初学者的嵌入式Qt开发指南,主要内容涵盖嵌入式Linux环境下Qt的安装配置、C++基础、Qt基础、多线程编程、网络编程、多媒体开发、数据库操作以及项目实战案例。文档从最简单的“Hello World”程序开始,逐步引导读者熟悉Qt开发环境的搭建、常用控件的使用、信号与槽机制、UI设计、数据处理等关键技术点。此外,文档还提供了详细的项目实战案例,如车牌识别系统的开发,帮助读者将理论知识应用于实际项目中。 适合人群:具备一定Linux和C++基础,希望快速入门嵌入式Qt开发的初学者或有一定开发经验的研发人员。 使用场景及目标: 1. **环境搭建**:学习如何在Ubuntu环境下搭建Qt开发环境,包括安装必要的工具和库。 2. **基础知识**:掌握C++面向对象编程、Qt基础控件的使用、信号与槽机制等核心概念。 3. **高级功能**:理解多线程编程、网络通信、多媒体处理、数据库操作等高级功能的实现方法。 4. **项目实战**:通过具体的项目案例(如车牌识别系统),巩固

    【人形机器人领域】宇树科技人形机器人技术实力与市场表现分析:科技创新与市场炒作的探讨

    内容概要:文章深入探讨了宇树科技人形机器人的技术实力、市场表现及未来前景,揭示其背后是科技创新还是市场炒作。宇树科技,成立于2016年,由90后创业者王兴兴创办,从四足机器人(如Laikago、AlienGo、A1)成功跨越到人形机器人(如H1和G1)。H1具有出色的运动能力和高精度导航技术,G1则专注于娱乐陪伴场景,具备模拟人手操作的能力。市场方面,宇树科技人形机器人因春晚表演而走红,但目前仅限于“极客型”用户购买,二手市场租赁价格高昂。文章认为,宇树科技的成功既源于技术突破,也离不开市场炒作的影响。未来,宇树科技将在工业、服务业、娱乐等多个领域拓展应用,但仍需克服成本、稳定性和安全等方面的挑战。 适合人群:对人工智能和机器人技术感兴趣的科技爱好者、投资者以及相关行业的从业者。 使用场景及目标:①了解宇树科技人形机器人的技术特点和发展历程;②分析其市场表现及未来应用前景;③探讨科技创新与市场炒作之间的关系。 阅读建议:本文详细介绍了宇树科技人形机器人的技术细节和市场情况,读者应关注其技术创新点,同时理性看待市场炒作现象,思考人形机器人的实际应用价值和发展潜力。

    C#3-的核心代码以及练习题相关

    C#3-的核心代码以及练习题相关

    MATLAB中基于麻雀搜索算法优化SVM分类的红酒数据集实现与解析

    内容概要:本文详细介绍了一种将麻雀搜索算法(SSA)用于优化支持向量机(SVM)分类的方法,并以红酒数据集为例进行了具体实现。首先介绍了数据预处理步骤,包括从Excel读取数据并进行特征和标签的分离。接着阐述了适应度函数的设计,采用五折交叉验证计算准确率作为评价标准。然后深入探讨了麻雀算法的核心迭代过程,包括参数初始化、种群更新规则以及如何通过指数衰减和随机扰动来提高搜索效率。此外,文中还提到了一些实用技巧,如保存最优参数以避免重复计算、利用混淆矩阵可视化分类结果等。最后给出了完整的代码框架及其在GitHub上的开源地址。 适合人群:具有一定MATLAB编程基础的研究人员和技术爱好者,尤其是对机器学习算法感兴趣的人士。 使用场景及目标:适用于需要解决多分类问题的数据科学家或工程师,旨在提供一种高效且易于使用的SVM参数优化方法,帮助用户获得更高的分类准确性。 其他说明:该方法不仅限于红酒数据集,在其他类似的数据集中同样适用。用户只需确保数据格式正确即可轻松替换数据源。

    MATLAB/Simulink中四分之一车被动悬架双质量模型的构建与分析

    内容概要:本文详细介绍了如何在MATLAB/Simulink环境中搭建四分之一车被动悬架双质量(二自由度)模型。该模型主要用于研究车辆悬架系统在垂直方向上的动态特性,特别是针对路面不平度引起的车轮和车身振动。文中不仅提供了具体的建模步骤,包括输入模块、模型主体搭建和输出模块的设计,还展示了如何通过仿真分析来评估悬架性能,如乘坐舒适性和轮胎接地性。此外,文章还讨论了一些常见的建模技巧和注意事项,如选择合适的求解器、处理代数环等问题。 适合人群:从事汽车动力学研究的科研人员、高校学生以及对车辆悬架系统感兴趣的工程师。 使用场景及目标:①用于教学目的,帮助学生理解车辆悬架系统的理论知识;②用于科研实验,验证不同的悬架设计方案;③用于工业应用,优化实际车辆的悬架系统设计。 其他说明:本文提供的模型基于MATLAB 2016b及以上版本,确保读者能够顺利重现所有步骤并获得预期结果。同时,文中附带了大量的代码片段和具体的操作指南,便于读者快速上手。

    COMSOL中光子晶体板谷态特性的建模与仿真方法

    内容概要:本文详细介绍了如何使用COMSOL软件进行光子晶体板谷态特性的建模与仿真。首先,定义了晶格常数和其他关键参数,如六边形蜂窝结构的创建、材料属性的设定以及周期性边界的配置。接下来,重点讲解了网格剖分的方法,强调了自适应网格和边界层细化的重要性。随后,讨论了如何通过参数扫描和频域分析来探索谷态特征,特别是在布里渊区高对称点附近观察到的能量带隙和涡旋结构。最后,提供了关于仿真收敛性和优化技巧的建议,确保结果的可靠性和准确性。 适合人群:从事光子学、电磁学及相关领域的研究人员和技术人员,尤其是对拓扑光子学感兴趣的学者。 使用场景及目标:适用于希望深入了解光子晶体板谷态特性的科研工作者,旨在帮助他们掌握COMSOL的具体应用方法,从而更好地进行相关实验和理论研究。 其他说明:文中不仅提供了详细的代码示例,还穿插了许多形象生动的比喻,使复杂的物理概念变得通俗易懂。同时,强调了仿真过程中需要注意的技术细节,如网格划分、边界条件设置等,有助于避免常见错误并提高仿真的成功率。

    微纳光学中金纳米球米氏散射的FDTD仿真及实验验证

    内容概要:本文详细介绍了利用有限差分时域法(FDTD)对金纳米球进行米氏散射仿真的全过程。首先,通过Python脚本设置了仿真环境,包括网格精度、材料参数、光源配置等。接着,展示了如何通过近场积分计算散射截面和吸收截面,并进行了远场角分布的仿真。文中还讨论了常见错误及其解决方法,如网格精度不足、边界条件不当等问题。最终,将仿真结果与米氏解析解进行了对比验证,确保了仿真的准确性。 适合人群:从事微纳光学研究的科研人员、研究生以及相关领域的工程师。 使用场景及目标:适用于需要精确模拟纳米颗粒与电磁波相互作用的研究项目,旨在提高仿真精度并验证理论模型。通过本文的学习,可以掌握FDTD仿真的具体实施步骤和技术要点。 其他说明:本文不仅提供了详细的代码示例,还分享了许多实践经验,帮助读者避免常见的仿真陷阱。同时强调了参数选择的重要性,特别是在纳米尺度下,每一个参数都需要精心调整以获得准确的结果。

    基数.txt

    基数

    2ddddddddddddddddddddddddddd

    2ddddddddddddddddddddddddddd

Global site tag (gtag.js) - Google Analytics