在閱讀Android的Framework處的代碼可以發現,無處不在SP給予了我視覺上的衝擊,這個是什嗎?初級的我,看這個當初就基本當成指標來用,熟不知其的內在美,於是在這裡和大家一起學習總結SP類的魅力所在。
1 SP這貨是個模板類,讓我們看下他的結構。
template class sp{public: inline sp() : m_ptr(0) { } sp(T* other); sp(const sp& other); template sp(U* other); template sp(const sp& other); ~sp(); // Assignment sp& operator = (T* other); sp& operator = (const sp& other); template sp& operator = (const sp& other); template sp& operator = (U* other); // Reset void clear(); // Accessors inline T& operator* () const { return *m_ptr; } inline T* operator-> () const { return m_ptr; } inline T* get() const { return m_ptr; } // Operators COMPARE(==) COMPARE(!=) COMPARE(>) COMPARE(<) COMPARE(<=) COMPARE(>=)private: template friend class sp; T* m_ptr;};
看到了上述的代碼結構,瞬間覺得其高大上,作為一個經典的模板類,精懂的人說這個類很好,其實沒有過多的去瞭解他只知道是更好的維護對象。這個SP指標內部有個T* m_ptr成員變數,它是真正指向我們new出來的變數。
我們以sp mA = new A();為例,作為一個模板類的建構函式,調用如下:
templatesp::sp(T* other) : m_ptr(other){ if (other) other->incStrong(this);}
果然,new出來的A對象被存入到sp的成員變數之中,這裡看到對mA對象構建時,會調用A的一個incStrong,這個是什嗎?閱讀過代碼的都知道一個RefBase類,他是比sp類更為常見的類,我們看她的結構可以發現內容都較多,但都是一些很特別的東西, 我們看下面的UML圖來分析我們之間構建的A為何要繼承與RefBase。
可以看到繼承了RefBase,那麼A的對象有東西可以玩了,調用RefBase的incStrong函數,接著看他的實現,先來個建構函式過過癮:
RefBase::RefBase() : mRefs(new weakref_impl(this)){}
做的東西不多,但是有一個成員變數mRefs,必然他先實現,好吧mRefs被賦值給了一個wekref_impl這個對象,傳入的this就是我們的這個new A()對象,好的接著再看看:
weakref_impl(RefBase* base) : mStrong(INITIAL_STRONG_VALUE) , mWeak(0) , mBase(base) , mFlags(0) { }
這裡有個mBase,他是一個RefBase類,故按C++的知識,一個從RefBase繼承的A類對象被賦值給了mBase,那基類的指標mBase就可以訪問類A內建的重載的虛函數了,先留著過會就會用到。
回來我們來看這個剛才other->incStrong(),調用的是基類的incStrong,看他的實現:
void RefBase::incStrong(const void* id) const{ weakref_impl* const refs = mRefs;//影子物件的refs refs->incWeak(id); refs->addStrongRef(id); const int32_t c = android_atomic_inc(&refs->mStrong); ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);#if PRINT_REFS ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);#endif if (c != INITIAL_STRONG_VALUE) { return; } android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); refs->mBase->onFirstRef();//mBase維護著繼承類實際對象this指標}
這裡是增加了對這個指標的引用次數,最重要的一點是關注最後一行代碼,在我們寫自己類的時候一般都喜歡重載該函數,在這裡面對類對象進行必要的初始化。
refs為mRefs,就是我們類A的對象的一個繼承成員weakref_impl,該對象有個內部成員mBase,通過上面的分析可知mBase就是指向類A對象的指標,故當訪問虛函數時,實際調用的是衍生類別的重載函數,故最終首次建立一個sp mA 時,就也是所謂的第一次引用吧。
2.學一學Android Native的Thread類
上面介紹了很多的類,在onFirstRef裡面可以經常會遇到一個run函數,這是啟動一個新線程的方法,那就是你的類A 繼承了thread類,從上一UML圖可以看到,thread有幾個核心的函數,最重要的就是一個run函數。來看她的部分代碼:
status_t Thread::run(const char* name, int32_t priority, size_t stack){ Mutex::Autolock _l(mLock); if (mRunning) { // thread already started return INVALID_OPERATION; } // reset status and exitPending to their default value, so we can // try again after an error happened (either below, or in readyToRun()) mStatus = NO_ERROR; mExitPending = false; mThread = thread_id_t(-1); // hold a strong reference on ourself mHoldSelf = this; mRunning = true; bool res; if (mCanCallJava) { res = createThreadEtc(_threadLoop, this, name, priority, stack, &mThread);//啟動_threadLoop線程 } else { res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } if (res == false) { mStatus = UNKNOWN_ERROR; // something happened! mRunning = false; mThread = thread_id_t(-1); mHoldSelf.clear(); // "this" may have gone away after this. return UNKNOWN_ERROR; } // Do not refer to mStatus here: The thread is already running (may, in fact // already have exited with a valid mStatus result). The NO_ERROR indication // here merely indicates successfully starting the thread and does not // imply successful termination/execution. return NO_ERROR; // Exiting scope of mLock is a memory barrier and allows new thread to run}
源碼上深入的話會比較複雜,但是一點可以肯定的是調用的線程函數是_thread_Loop,看看他的實現:
int Thread::_threadLoop(void* user){ Thread* const self = static_cast(user);//衍生類別對象轉為基類指標do { bool result; if (first) { first = false; self->mStatus = self->readyToRun();//直接調用繼承類的readyToRun result = (self->mStatus == NO_ERROR);...... else result = self->threadLoop(); }}
這裡的self是什麼,實際是我們建立的一個對象,那麼首次就是調用類A的操作函數readyToRun(),隨後一般是調用threadLoop進入線程的迴圈,線程的退出主要由threadLoop函數的返回結果來決定。
3. 好了,下面再來看看這個這幾個比較常見的過程,會和C++的多態符合重載有緊密的關係。
比如mA->fun(),一眼看去感覺和普通的指標調用沒有區別,但你是否知道mA只是一個sp類的對象,那麼這個mA->的操作符是什嗎?那就是所謂的符合重載,來看sp的幾個運算子的重載函數:
inline T& operator* () const { return *m_ptr; } inline T* operator-> () const { return m_ptr; } inline T* get() const { return m_ptr; }
很好,可以看到他的實現,不需要輸入參數,返回一個m_ptr,m_ptr是什麼,前面說過他是我們new A()出來的一個對象,那麼調用的就是類A所屬的成員函數.類似的還有:
templatesp& sp::operator = (T* other){ if (other) other->incStrong(this); if (m_ptr) m_ptr->decStrong(this); m_ptr = other; return *this;}template templatesp& sp::operator = (const sp& other){ T* otherPtr(other.m_ptr); if (otherPtr) otherPtr->incStrong(this); if (m_ptr) m_ptr->decStrong(this); m_ptr = otherPtr; return *this;}
分別是對指標與引用的賦值操作。
上述的Android Framework裡面會經常遇到這些細節性的問題,瞭解這些基本類往往可以做到事半功倍,閱讀代碼更加心領神會!