android 中的的 sp/wp/RefBase

2023-11-18

转自:http://blog.csdn.net/innost/article/details/6752443

5.1 概述

初次接触Android源码时,见到最多的一定是sp和wp。即使你只是沉迷于Java世界的编码,那么Looper和Handler也是避不开的。本章的目的,就是把经常碰到的这些内容中的“拦路虎”一网打尽,将它们彻底搞懂。至于弄明白它们有什么好处,就仁者见仁,智者见智了。个人觉得Looper和Handler相对会更实用一些。
5.2 以“三板斧”揭秘RefBase、sp和wp
RefBase是Android中所有对象的始祖,类似于MFC中的CObject及Java中的Object对象。在Android中,RefBase结合sp和wp,实现了一套通过引用计数的方法来控制对象生命周期的机制。就如我们想像的那样,这三者的关系非常暧昧。初次接触Android源码的人往往会被那个随处可见的sp和wp搞晕了头。
什么是sp和wp呢?其实,sp并不是我开始所想的smart pointer(C++语言中有这个东西),它真实的意思应该是strong pointer,而wp则是weak pointer的意思。我认为,Android推出这一套机制可能是模仿Java,因为Java世界中有所谓weak reference之类的东西。sp和wp的目的,就是为了帮助健忘的程序员回收new出来的内存。
说明 我还是喜欢赤裸裸地管理内存的分配和释放。不过,目前sp和wp的使用已经深入到Android系统的各个角落,想把它去掉真是不太可能了。
这三者的关系比较复杂,都说程咬金的“三板斧”很厉害,那么我们就借用这三板斧,揭密其间的暧昧关系。
5.2.1 第一板斧—初识影子对象
我们的“三板斧”,其实就是三个例子。相信这三板斧劈下去,你会很容易理解它们。
[-->例子1]
//类A从RefBase派生,RefBase是万物的始祖。
class A:public RefBase
{
 //A没有任何自己的功能。
}
int main()
{
  A* pA = new A;
  {
   //注意我们的sp、wp对象是在{}中创建的,下面的代码先创建sp,然后创建wp。
   sp<A> spA(pA);
   wp<A> wpA(spA);
    //大括号结束前,先析构wp,再析构sp。
   }
}
例子够简单吧?但也需一步一步分析这斧子是怎么劈下去的。
1. RefBase和它的影子
类A从RefBase中派生。使用的是RefBase构造函数。代码如下所示:
[-->RefBase.cpp]
RefBase::RefBase()
    : mRefs(new weakref_impl(this))//注意这句话
{
  //mRefs是RefBase的成员变量,类型是weakref_impl,我们暂且叫它影子对象。
  //所以A有一个影子对象。
}
mRefs是引用计数管理的关键类,需要进一步观察。它是从RefBase的内部类weakref_type中派生出来的。
先看看它的声明:
class RefBase::weakref_impl : public RefBase::weakref_type
//从RefBase的内部类weakref_type派生。
由于Android频繁使用C++内部类的方法,所以初次阅读Android代码时可能会有点不太习惯,C++的内部类和Java的内部类相似,但有一点不同,即它需要一个显式的成员指向外部类对象,而Java的内部类对象有一个隐式的成员指向外部类对象的。
说明 内部类在C++中的学名叫nested class(内嵌类)。
[-->RefBase.cpp::weakref_imple构造]
weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE) //强引用计数,初始值为0x1000000。
        , mWeak(0) //弱引用计数,初始值为0。
        , mBase(base)//该影子对象所指向的实际对象。
        , mFlags(0)
        , mStrongRefs(NULL)
        , mWeakRefs(NULL)
        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
        , mRetain(false)
    {
     }
如你所见,new了一个A对象后,其实还new了一个weakref_impl对象,这里称它为影子对象,另外我们称A为实际对象。
这里有一个问题:影子对象有什么用?
可以仔细想一下,是不是发现影子对象成员中有两个引用计数?一个强引用,一个弱引用。如果知道引用计数和对象生死有些许关联的话,就容易想到影子对象的作用了。
说明 按上面的分析来看,在构造一个实际对象的同时,还会悄悄地构造一个影子对象,在嵌入式设备的内存不是很紧俏的今天,这个影子对象的内存占用已经不成问题了。
2.sp上场
程序继续运行,现在到了:
sp<A> spA(pA);
请看sp的构造函数,它的代码如下所示(注意,sp是一个模板类,对此不熟悉的读者可以去翻翻书,或者干脆把所有出现的T都换成A):
[-->RefBase.h::sp(T* other)]
template<typename T>
sp<T>::sp(T* other) //这里的other就是刚才创建的pA。
    : m_ptr(other)// sp保存了pA的指针。
{
    if (other) other->incStrong(this);//调用pA的incStrong。
}
OK,战场转到RefBase的incStrong中。它的代码如下所示:
[-->RefBase.cpp]
void RefBase::incStrong(const void* id) const
{
 //mRefs就是刚才在RefBase构造函数中new出来的影子对象。
 weakref_impl* const refs = mRefs; 


//操作影子对象,先增加弱引用计数。
 refs->addWeakRef(id);
 refs->incWeak(id);
 ......
先来看看影子对象的这两个weak函数都干了些什么。   
(1)眼见而心不烦
下面看看第一个函数addWeakRef,代码如下所示:
[-->RefBase.cpp]
void addWeakRef(const void* /*id*/) { }
呵呵,addWeakRef啥都没做,因为这是release版走的分支。调试版的代码我们就不讨论了,它是给创造RefBase、 sp,以及wp的人调试用的。
说明 调试版分支的代码很多,看来创造它们的人也在为不理解它们之间的暧昧关系痛苦不已。
总之,一共有这么几个不用考虑的函数,下面都已列出来了。以后再碰见它们,干脆就直接跳过去:
void addStrongRef(const void* /*id*/) { }
void removeStrongRef(const void* /*id*/) { }
void addWeakRef(const void* /*id*/) { }
void removeWeakRef(const void* /*id*/) { }
void printRefs() const { }
void trackMe(bool, bool) { }
继续我们的征程。再看incWeak函数,代码如下所示:
[-->RefBase.cpp]
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);  //上面说了,非调试版什么都不干。
   const int32_t c = android_atomic_inc(&impl->mWeak);
  //原子操作,影子对象的弱引用计数加1。
  //千万记住影子对象的强弱引用计数的值,这是彻底理解sp和wp的关键。
}
好,我们再回到incStrong,继续看代码:
[-->RefBase.cpp]
   ......
  //刚才增加了弱引用计数。
  //再增加强引用计数。
  refs->addStrongRef(id); //非调试版这里什么都不干。
  //下面函数为原子加1操作,并返回旧值。所以c=0x1000000,而mStrong变为0x1000001。
   const int32_t c = android_atomic_inc(&refs->mStrong);
   if (c != INITIAL_STRONG_VALUE)  {
      //如果c不是初始值,则表明这个对象已经被强引用过一次了。
        return;
    }
  //下面这个是原子加操作,相当于执行refs->mStrong +(-0x1000000),最终mStrong=1。
  android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
 /*
   如果是第一次引用,则调用onFirstRef,这个函数很重要,派生类可以重载这个函数,完成一些
   初始化工作。
 */
 const_cast<RefBase*>(this)->onFirstRef();
}
说明 android_atomic_xxx是Android平台提供的原子操作函数,原子操作函数是多线程编程中的常见函数,读者可以学习原子操作函数的相关知识,本章后面也会对其进行介绍。
(2)sp构造的影响
sp构造完后,它给这个世界带来了什么?
那就是在RefBase中影子对象的强引用计数变为1,且弱引用计数也变为1。
更准确的说法是,sp的出生导致影子对象的强引用计数加1,且弱引用计数也加1。
(3)wp构造的影响
继续看wp,例子中的调用方式如下:
wp<A> wpA(spA)
wp有好几个构造函数,原理都一样。来看这个最常见的:
[-->RefBase.h::wp(const sp<T>& other)]
template<typename T>
wp<T>::wp(const sp<T>& other)
    : m_ptr(other.m_ptr) //wp的成员变量m_ptr指向实际对象。
{
    if (m_ptr) {
       //调用pA的createWeak,并且保存返回值到成员变量m_refs中。
        m_refs = m_ptr->createWeak(this); 
    }
}
[-->RefBase.cpp]
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
//调用影子对象的incWeak,这个我们刚才讲过了,它会导致影子对象的弱引用计数增加1。
 mRefs->incWeak(id); 
 return mRefs;  //返回影子对象本身。
}
我们可以看到,wp化后,影子对象的弱引用计数将增加1,所以现在弱引用计数为2,而强引用计数仍为1。另外,wp中有两个成员变量,一个保存实际对象,另一个保存影子对象。sp只有一个成员变量,用来保存实际对象,但这个实际对象内部已包含了对应的影子对象。
OK,wp创建完了,现在开始进行wp的析构。
(4)wp析构的影响
wp进入析构函数,则表明它快要离世了,代码如下所示:
[-->RefBase.h]
template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this); //调用影子对象的decWeak,由影子对象的基类实现。
}
[-->RefBase.cpp]
void RefBase::weakref_type::decWeak(const void* id)
{
  //把基类指针转换成子类(影子对象)的类型,这种做法有些违背面向对象编程的思想。
  weakref_impl* const impl = static_cast<weakref_impl*>(this);
  impl->removeWeakRef(id);//非调试版不做任何事情。


  //原子减1,返回旧值,c=2,而弱引用计数从2变为1。
  const int32_t c = android_atomic_dec(&impl->mWeak);
  if (c != 1) return; //c=2,直接返回。
   
  //如果c为1,则弱引用计数为0,这说明没用弱引用指向实际对象,需要考虑是否释放内存。
  // OBJECT_LIFETIME_XXX和生命周期有关系,我们后面再说。
    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
        if (impl->mStrong == INITIAL_STRONG_VALUE)
            delete impl->mBase;
        else {
            delete impl;
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
            delete impl->mBase;
        }
    }
}
在例1中,wp析构后,弱引用计数减1。但由于此时强引用计数和弱引用计数仍为1,所以没有对象被干掉,即没有释放实际对象和影子对象占据的内存。
(5)sp析构的影响
下面进入sp的析构。
[-->RefBase.h]
template<typename T>
sp<T>::~sp()
{
    if (m_ptr) m_ptr->decStrong(this); //调用实际对象的decStrong,由RefBase实现。
}
[-->RefBase.cpp]
void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs; 
    refs->removeStrongRef(id);//调用影子对象的removeStrongRef,啥都不干。
    //注意,此时强弱引用计数都是1,下面函数调用的结果是c=1,强引用计数为0。
    const int32_t c = android_atomic_dec(&refs->mStrong);
    if (c == 1) { //对于我们的例子, c为1
        //调用onLastStrongRef,表明强引用计数减为0,对象有可能被delete。
        const_cast<RefBase*>(this)->onLastStrongRef(id);
       //mFlags为0,所以会通过delete this把自己干掉。
      //注意,此时弱引用计数仍为1。
        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
            delete this; 
        }
   ......
}
先看delete this的处理,它会导致A的析构函数被调用。再来看A的析构函数,代码如下所示:
[-->例子1::~A()]
//A的析构直接导致进入RefBase的析构。
RefBase::~RefBase()
{
   if (mRefs->mWeak == 0) { //弱引用计数不为0,而是1。
       delete mRefs;   
    }
}
RefBase的delete this自杀行为没有把影子对象干掉,但我们还在decStrong中,可从delete this接着往下看:
[-->RefBase.cpp]
     .... //接前面的delete this
   if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
            delete this;
        }
  //注意,实际数据对象已经被干掉了,所以mRefs也没有用了,但是decStrong刚进来
  //的时候就把mRefs保存到refs了,所以这里的refs指向影子对象。
    refs->removeWeakRef(id); 
    refs->decWeak(id);//调用影子对象decWeak
}
[-->RefBase.cpp]
void RefBase::weakref_type::decWeak(const void* id)
{
  weakref_impl* const impl = static_cast<weakref_impl*>(this);
  impl->removeWeakRef(id);//非调试版不做任何事情。


    //调用前影子对象的弱引用计数为1,强引用计数为0,调用结束后c=1,弱引用计数为0。
    const int32_t c = android_atomic_dec(&impl->mWeak);
    if (c != 1) return;
    
    //这次弱引用计数终于变为0了,并且mFlags为0, mStrong也为0。
    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
        if (impl->mStrong == INITIAL_STRONG_VALUE)
            delete impl->mBase;
        else {
            delete impl; //impl就是this,把影子对象也就是自己干掉。
        }
    } else {
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
            delete impl->mBase;
        }
    }
}
好,第一板斧劈下去了!来看看它的结果是什么。
3.第一板斧的结果
第一板斧过后,来总结一下刚才所学的知识:
 RefBase中有一个隐含的影子对象,该影子对象内部有强弱引用计数。
 sp化后,强弱引用计数各增加1,sp析构后,强弱引用计数各减1。
 wp化后,弱引用计数增加1,wp析构后,弱引用计数减1。
完全彻底地消灭RefBase对象,包括让实际对象和影子对象灭亡,这些都是由强弱引用计数控制的,另外还要考虑flag的取值情况。当flag为0时,可得出如下结论:
 强引用为0将导致实际对象被delete。
 弱引用为0将导致影子对象被delete。
5.2.2 第二板斧—由弱生强
再看第二个例子,代码如下所示:
[-->例子2]
int main()
{
   A *pA = new A();
   wp<A> wpA(pA);
   sp<A> spA = wpA.promote();//通过promote函数,得到一个sp。
}
对A的wp化,不再做分析了。按照前面所讲的知识,wp化后仅会使弱引用计数加1,所以此处wp化的结果是:
影子对象的弱引用计数为1,强引用计数仍然是初始值0x1000000。
wpA的promote函数是从一个弱对象产生一个强对象的重要函数,试看—
1. 由弱生强的方法
代码如下所示:
[-->RefBase.h]
template<typename T>
sp<T> wp<T>::promote() const
{
    return sp<T>(m_ptr, m_refs);  //调用sp的构造函数。
}
[-->RefBase.h]
template<typename T>
sp<T>::sp(T* p, weakref_type* refs)
    : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)//有点看不清楚。
{
//上面那行代码够简洁,但是不方便阅读,我们写成下面这样:
/*
  T* pTemp = NULL;
  //关键函数attemptIncStrong
  if(p != NULL && refs->attemptIncStrong(this) == true)
      pTemp = p;


  m_ptr = pTemp;
*/
}
2.成败在此一举
由弱生强的关键函数是attemptIncStrong,它的代码如下所示:
[-->RefBase.cpp]
bool RefBase::weakref_type::attemptIncStrong(const void* id)
{
     incWeak(id); //增加弱引用计数,此时弱引用计数变为2。
     weakref_impl* const impl = static_cast<weakref_impl*>(this);
      int32_t curCount = impl->mStrong; //这个仍是初始值。
     //下面这个循环,在多线程操作同一个对象时可能会循环多次。这里可以不去管它,
     //它的目的就是使强引用计数增加1。
    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
        if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
            break;
        }
        curCount = impl->mStrong;
    }
    
    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
         bool allow;
  /*
   下面这个allow的判断极为精妙。impl的mBase对象就是实际对象,有可能已经被delete了。
   curCount为0,表示强引用计数肯定经历了INITIAL_STRONG_VALUE->1->...->0的过程。
   mFlags就是根据标志来决定是否继续进行||或&&后的判断,因为这些判断都使用了mBase,
   如不做这些判断,一旦mBase指向已经回收的地址,你就等着segment fault吧!
   其实,咱们大可不必理会这些东西,因为它不影响我们的分析和理解。
  */
        if (curCount == INITIAL_STRONG_VALUE) {
             allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
                  || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
        } else {
            allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
                  && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
        }
        if (!allow) {
        //allow为false,表示不允许由弱生强,弱引用计数要减去1,这是因为咱们进来时加过一次。
            decWeak(id);
            return false; //由弱生强失败。
        }


     //允许由弱生强,强引用计数要增加1,而弱引用计数已经增加过了。
        curCount = android_atomic_inc(&impl->mStrong);
        if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
            impl->mBase->onLastStrongRef(id);
        }
    }
    impl->addWeakRef(id);
    impl->addStrongRef(id);//两个函数调用没有作用。
     if (curCount == INITIAL_STRONG_VALUE) {
         //强引用计数变为1。
        android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
        //调用onFirstRef,通知该对象第一次被强引用。
        impl->mBase->onFirstRef();
    }
    return true; //由弱生强成功。
}
3. 第二板斧的结果
promote完成后,相当于增加了一个强引用。根据上面所学的知识可知:
由弱生强成功后,强弱引用计数均增加1。所以现在影子对象的强引用计数为1,弱引用计数为2。
5.2.3 第三板斧—破解生死魔咒
1. 延长生命的魔咒
RefBase为我们提供了一个这样的函数:
extendObjectLifetime(int32_t mode)
另外还定义了一个枚举:
enum {
        OBJECT_LIFETIME_WEAK    =  0x0001, 
        OBJECT_LIFETIME_FOREVER = 0x0003
};
注意:FOREVER的值是3,用二进制表示是B11,而WEAK的二进制是B01,也就是说FOREVER包括了WEAK的情况。
上面这两个枚举值,是破除强弱引用计数作用的魔咒。先观察flags为OBJECT_LIFETIME_ WEAK的情况,见下面的例子。
[-->例子3]
class A:public RefBase
{
   public A()
   {
      extendObjectLifetime(OBJECT_LIFETIME_WEAK);//在构造函数中调用。
   }
}
int main()
{
   A *pA = new A();
    wp<A> wpA(pA);//弱引用计数加1。
  {
      sp<A> spA(pA) //sp后,结果是强引用计数为1,弱引用计数为2。
   }
   ....
}
sp的析构将直接调用RefBase的decStrong,它的代码如下所示:
[-->RefBase.cpp]
void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = android_atomic_dec(&refs->mStrong);
    if (c == 1) { //上面进行原子操作后,强引用计数为0
        const_cast<RefBase*>(this)->onLastStrongRef(id);
        //注意这句话。如果flags不是WEAK或FOREVER的话,将delete 数据对象。
       //现在我们的flags是WEAK,所以不会delete 它。
        if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
            delete this;
        }
  }
    refs->removeWeakRef(id);
    refs->decWeak(id);//调用前弱引用计数是2。
}
然后调用影子对象的decWeak。再来看它的处理,代码如下所示:
[-->RefBase.cpp::weakref_type的decWeak()函数]
void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);
    const int32_t c = android_atomic_dec(&impl->mWeak);
    if (c != 1) return;  //c为2,弱引用计数为1,直接返回。 
   /* 
     假设我们现在到了例子中的wp析构之处,这时也会调用decWeak,在调用上面的原子减操作后
     c=1,弱引用计数变为0,此时会继续往下运行。由于mFlags为WEAK ,所以不满足if的条件。
   */
    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
        if (impl->mStrong == INITIAL_STRONG_VALUE)
            delete impl->mBase;
        else {
            delete impl;
        }
    } else {//flag为WEAK,满足else分支的条件。
        impl->mBase->onLastWeakRef(id);
       /*
        由于 flags值满足下面这个条件,所以实际对象会被delete,根据前面的分析可知,实际对象的delete会检查影子对象的弱引用计数,如果它为0,则会把影子对象也delete掉。
        由于影子对象的弱引用计数此时已经为0,所以影子对象也会被delete。
      */
        if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
            delete impl->mBase;
        }
    }
}
2. LIFETIME_WEAK的魔力
看完上面的例子,我们发现什么了?
在LIFETIME_WEAK的魔法下,强引用计数为0,而弱引用计数不为0的时候,实际对象没有被delete!只有当强引用计数和弱引用计数同时为0时,实际对象和影子对象才会被delete。
3. 魔咒大揭秘
至于LIFETIME_FOREVER的破解,就不用再来一斧子了,我直接给出答案:
 flags为0,强引用计数控制实际对象的生命周期,弱引用计数控制影子对象的生命周期。强引用计数为0后,实际对象被delete。所以对于这种情况,应记住的是,使用wp时要由弱生强,以免收到segment fault信号。
 flags为LIFETIME_WEAK,强引用计数为0,弱引用计数不为0时,实际对象不会被delete。当弱引用计数减为0时,实际对象和影子对象会同时被delete。这是功德圆满的情况。
 flags为LIFETIME_FOREVER,对象将长生不老,彻底摆脱强弱引用计数的控制。所以你要在适当的时候杀死这些“老妖精”,免得她祸害“人间”。
5.2.4 轻量级的引用计数控制类LightRefBase
上面介绍的RefBase,是一个重量级的引用计数控制类。那么,究竟有没有一个简单些的引用计数控制类呢?Android为我们提供了一个轻量级的LightRefBase。这个类非常简单,我们不妨一起来看看。
[-->RefBase.h]
template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(const void* id) const {
      //LightRefBase只有一个引用计数控制量mCount。incStrong的时候使它增加1。
        android_atomic_inc(&mCount);
    }
inline void decStrong(const void* id) const {
       //decStrong的时候减1,当引用计数变为零的时候,delete掉自己。
        if (android_atomic_dec(&mCount) == 1) {
            delete static_cast<const T*>(this);
        }
    }
    inline int32_t getStrongCount() const {
        return mCount;
    }
    
protected:
    inline ~LightRefBase() { }
    
private:
mutable volatile int32_t mCount;//引用计数控制变量。
};
LightRefBase类够简单吧?不过它是一个模板类,我们该怎么用它呢?下面给出一个例子,其中类A是从LightRefBase派生的,写法如下:
class A:public LightRefBase<A> //注意派生的时候要指明是LightRefBase<A>。
{
public:
A(){};
~A(){};
}; 
另外,我们从LightRefBase的定义中可以知道,它支持sp的控制,因为它只有incStrong和decStrong函数。
5.2.5 题外话—三板斧的来历
从代码量上看,RefBase、sp和wp的代码量并不多,但里面的关系,尤其是flags的引入,曾一度让我眼花缭乱。当时,我确实很希望能自己调试一下这些例子,但在设备上调试native代码,需要花费很大的精力,即使是通过输出log的方式来调试也需要花很多时间。该怎么解决这一难题?
既然它的代码不多而且简单,那何不把它移植到台式机的开发环境下,整一个类似的RefBase呢?有了这样的构想,我便用上了Visual Studio。至于那些原子操作,Windows平台上有很直接的InterlockedExchangeXXX与之对应,真的是踏破铁鞋无觅处,得来全不费功夫!(在Linux平台上,不考虑多线程的话,将原子操作换成普通的非原子操作不是也可以吗?如果更细心更负责任的话,你可以自己用汇编来实现常用的原子操作,内核代码中有现成的函数,一看就会明白。)
如果把破解代码看成是攻城略地的话,我们必须学会灵活多变,而且应力求破解方法日臻极致!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

android 中的的 sp/wp/RefBase 的相关文章

随机推荐

  • 春秋云镜 CVE-2022-32991

    春秋云镜 CVE 2022 32991 Web Based Quiz System SQL注入 靶标介绍 该CMS的welcome php中存在SQL注入攻击 1 启动场景 2 注册任意用户 3 登录成功进入主页 http eci 2zei
  • postgresql 清空数据表 truncate

    在 mysql 中如果需要清空表 只需要 TRUNCATE table name 即可 如果有自增的 id 字段 也会还原回 1 但是 postgresql 与 mysql 稍有不同 postgresql 的自增字段 是通过 序列 sequ
  • Python Turtle绘图基础(三)——Turtle色彩和画笔设置

    今天继续给大家介绍Python相关知识 本文主要内容是Python Turtle绘图基础 三 包括Turtle色彩和Turtle画笔设置 一 Turtle色彩设置 想要用Turtle绘制出更加复杂 更加美丽的图形 就必须学习Turtle的色
  • 【按照年月去统计信息并分类展示】

    1 前言 需求是需要将历史订单按照年月分类展示 并展示汇总值 由于后端返回的是数组的数据 并没有将数据做好统计分类 出于对自己的自信以及不想给别人添麻烦的信息 然后自己写了一下处理的方法 然后放上最后的效果图吧 2 代码实现 按照月份统计每
  • 跟着英雄刷算法-素数

    跟着英雄大佬刷算法的第三天 数论基础 优化一 对于一个非素数n来说 如果x是n的一个因子 那么n x也是n的一个因子 我们可以假设x 所以对于一个数n 判断它是否为一个素数我们需要确定的范围为 2 根号下n 优化二 例1 不是素数返回0 b
  • Tomcat 8 解决“At least one JAR was scanned for TLDs yet contained no TLDs”问题

    参考 http stackoverflow com questions 14375673 how to fix jsp compiler warning one jar was scanned for tlds yet contained
  • 网络层抓包tcpdump

    sudo tcpdump i eth0 s 0 nn host iphost w xxx pcap 这段代码使用了命令行工具 tcpdump 用于在Linux系统上捕获网络数据包 让我详细介绍一下这段代码的含义和 tcpdump 的用法 代
  • 2022年4月3日-4月15日(方案A,ogremain源码抄写+ue4视频学习,共22小时,合计1270小时,剩8730小时)

    截至2022年4月1日 ogreMain剩下4533行 含注释 纯代码2646行 周二时学完了ue第五套视频教程编辑器1 good 接下来 UE4视频教程进行到了mysql 1 1 tf1 2 1 oss 4 2 simpleThread
  • c语言/c++(数据结构篇) 之 括号匹配检验实例(栈和队列)(4/7)

    实验目的及要求 熟悉掌握利用栈完成括号是否匹配的检验 实验内容 利用栈完成括号匹配检验 目的 掌握栈的后进先出原则在解决实际问题中的应用 内容 输入一组括号 构造栈 利用栈判断所输入的括号是否匹配 并能够输出匹配 多左括号 多右括号三种不同
  • Centos7Hadoop集群搭建准备工作

    设置静态IP地址 1 vi etc sysconfig network scripts ifcfg ens33 BOOTPROTO static ONBOOT yes 固定IP IPADDR IP地址 子网掩码 NETMASK 255 25
  • [思维模式-8]:《如何系统思考》-4- 认识篇 - 什么是系统思考?系统思考的特征?系统思考的思维转变。

    目录 第1章 系统思考概述 1 1 什么是系统思考 1 2 系统思考适合解决什么样的问题 解决复杂问题的有效利器 1 3 思维模式的转换 还原论向整体论 西医向中医 第2章 系统思考的四项特征 2 1 看到全貌而非局部 2 2 看透结构而非
  • 基于 Kintex-7 FPGA + Nvidia TX2 = 16通道高速ADC数据采集系统

    在之前接触的设计中如果涉及要实现ADC采样的话 往往会从精度和速率来考虑对性能的影响 一般来说精度是固定的或有一个最大精度设置 但是采样速率的话 过快会造成采样不准确 往往会对整个设计的性能造成限制 所以一直期望有这样一个系统 可以实现高速
  • 如何配置Java环境变量

    文章目录 零 首先需要进入环境变量页面 一 配置 JAVA HOME 变量 二 配置 Path 变量 三 配置 CLASSPATH 变量 四 验证是否配置成功 零 首先需要进入环境变量页面 1 win i 打开设置页面 点击系统 2 点击关
  • Camera的学习笔记(二)——ISP

    ISP概念 ISP是Image Signal Processor的缩写 全称是影像处理器 在相机成像的整个环节中 它负责接收感光元件 Sensor 的原始信号数据 可以理解为整个相机拍照 录像的第一步处理流程 对图像质量起着非常重要的作用
  • 改善服务器响应时间,一种改进WWW服务器响应时间的调度方法

    一种改进WWW服务器响应时间的调度方法 这篇论文提出了一种基于控制因子 处于先来先服务和最短作业优先调度方法之间的分类调度方法 它是非抢占的 且不会发生HTTP请求长期等待而未得到WW 本文共3页 阅读全文 gt gt 公共交通是城市居民出
  • element UI 对导航el-menu 样式的修改

    element UI 对导航el menu 样式的修改 对样式进行修改时 el menu horizontal gt el submenu el submenu title el menu horizontal gt el submenu
  • 关于unity3的中关于创建方法的总结

    关于创建基本物体 有些情况会使用上 物体碰撞 游戏里怪物和英雄的触发事件上 创建一个简单物体 隐藏mesh可以作为简单的触法器使用 多次创建预制体Prefab 方法Instantiate original Object position V
  • 基于蜣螂算法优化的SVM数据分类预测-附代码

    基于蜣螂算法优化的SVM数据分类预测 附代码 文章目录 基于蜣螂算法优化的SVM数据分类预测 附代码 1 数据集 2 SVM模型建立 3 基于蜣螂算法优化的SVM 4 测试结果 5 参考文献 6 Matlab代码 7 python代码 摘要
  • IP数据包格式各字段详解说明

    1 版本 指IP协议的版本 为0100或0110 即IPv4和IPv6两种版本 通信双方使用的IP协议版本必须一致 2 首部长度 占 4 位 可表示的最大十进制数值是15 请注意 这个字段所表示数的单位是32位字 即0001表示1个32位字
  • android 中的的 sp/wp/RefBase

    转自 http blog csdn net innost article details 6752443 5 1 概述 初次接触Android源码时 见到最多的一定是sp和wp 即使你只是沉迷于Java世界的编码 那么Looper和Hand